Annotation overview
What is annotation
The most common annotations are those in the Spring framework, such as @ RequestMapping @Controller. What is the role of annotations? Why use annotations?
Annotation is like a label, which can mark classes, attributes and methods. The marked content can use the getannotation (class < T >) method of the corresponding object (Class/Method/Field) to obtain the annotation marked on the current content, so as to make corresponding operations.
Why do you use annotations? This is about AOP (aspect oriented programming). AOP is actually the supplement and improvement of OOP (object oriented programming), and object-oriented programming is a top-down relationship of complete things. When some specific businesses need a unified pre-processing or post-processing, such as permission control Log printing, interface access statistics and so on. Object-oriented will often lead to a large number of code duplication, and the project complexity and coupling will increase greatly. In this case, tangential programming came into being. AOP uses a concept called "crosscutting". AOP note links need to be added
format
Basic format
public @interface AnnotationName { }
Add attribute
You can also add attributes in the annotation class (it looks like a method, but it is actually equivalent to attributes)
String value() default "";
Using annotations
Use annotations and assign values to attributes. When only one value is given and the value is value, the attribute name can be omitted.
@AnnotationName(value = "value") // be equal to @AnnotationName("value")
Limit usage location and scope
Limit the use position of the annotation, and add the @ Target() annotation on the annotation class. This annotation accepts a single ElementType attribute or array.
- TYPE: can be used on classes, interfaces or enumeration classes
- FIELD: can be used in attribute
- METHOD: can be used in the METHOD
- PARAMETER: can be used in method parameters
- CONSTRUCTOR: can be used in construction parameters
- LOCAL_VARIABLE: can be used in local variables
- ANNOTATION_TYPE: can be used in annotation class
@Target({ElementType.TYPE, ElementType.Field})
Limit the effective scope of the annotation, add @ Retention() annotation on the annotation class, and this annotation accepts a single RetentionPolicy attribute.
- SOURCE: annotations exist only at the SOURCE level and will be ignored and deleted after compilation
- CLASS: annotations exist after compilation, but they will be ignored and deleted after the CLASS is loaded into memory
- RUNTIME: annotations still exist when the program runs after the class is loaded. Only annotations in this state will be reflected and read
@Retention(RetentionPolicy.RUNTIME)
Annotation case
junit unit test
The main function of unit test is to have pre post methods. The following code simply implements the main functions of junit.
Annotation class code
- MoBefore: mark on pre execution method
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MoBefore { String value() default ""; }
- MoAfter: Post execution method tag
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MoAfter { String value() default ""; }
- MoTest: Test Method tag
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MoTest { String value() default ""; }
- MoJunit: contains test method class tags
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MoJunit { String value() default ""; }
Test class
- NoCanRunException: custom exception
public class NoCanRunException extends RuntimeException { public NoCanRunException(String msg){ super(msg); } }
- AnnoTargetClass: the target test class to run
@MoJunit public class AnnoTargetClass { @MoBefore("is before") public void MoRunBefore(){ System.out.println("run mo run before"); } @MoTest("this is MoRunTest method") public void MoRunTest(){ System.out.println("run mo run test"); } @MoAfter("is after") public void MoRunAfter(){ System.out.println("run mo run after"); } }
main method
- Main class
public class MoJunitTestFrameWork { public static final Logger log = LoggerFactory.getLogger(MoJunitTestFrameWork.class); public static void main(String[] args) { try { // Scan the specified package String packageName = "com.mochen.advance.annotation.junit.test"; // Get all class names under the package List<String> classNames = getClassNames(packageName); for (String className : classNames) { Class<?> clazz = Class.forName(packageName + "." +className); // Get the class with MoJunit annotation MoJunit annotation = clazz.getAnnotation(MoJunit.class); if (annotation == null) continue; Object o = clazz.newInstance(); // instantiation // Gets the method of all specified annotations List<Map<String, Object>> runMethodList = new ArrayList<>(); List<Map<String, Object>> beforeMethodList = new ArrayList<>(); List<Map<String, Object>> afterMethodList = new ArrayList<>(); Method[] methods = clazz.getMethods(); for (Method method : methods) { Map<String, Object> map = new HashMap<>(); MoBefore annotationMoBefore = method.getAnnotation(MoBefore.class); if (annotationMoBefore != null){ map.put("value", annotationMoBefore.value()); map.put("method", method); beforeMethodList.add(map); continue; } MoTest annotationMoTest = method.getAnnotation(MoTest.class); if (annotationMoTest != null){ map.put("value", annotationMoTest.value()); map.put("method", method); runMethodList.add(map); continue; } MoAfter annotationMoAfter = method.getAnnotation(MoAfter.class); if (annotationMoAfter != null){ map.put("value", annotationMoAfter.value()); map.put("method", method); afterMethodList.add(map); } } // If the obtained test method is empty, an exception is thrown if (runMethodList.size() <= 0){ throw new NoCanRunException("this class is @MoJunit class, but no @MoTest annotation"); } for (Map<String, Object> runMap : runMethodList) { // Execute pre method for (Map<String, Object> beforeMap : beforeMethodList) { log.info("before run value => {}", beforeMap.get("value")); ((Method) beforeMap.get("method")).invoke(o); } // Execution test method log.info("test run value => {}", runMap.get("value")); ((Method) runMap.get("method")).invoke(o); // Execute post method for (Map<String, Object> afterMap : afterMethodList) { log.info("after run value => {}", afterMap.get("value")); ((Method) afterMap.get("method")).invoke(o); } } } } catch (Exception e) { e.printStackTrace(); } } public static List<String> getClassNames(String packageName) throws IOException { List<String> classNameList = new ArrayList<>(); // Package relative path String packagePath = packageName.replace(".", "/"); // Resource URL URL url = ClassLoader.getSystemResource(""); // The asking price agreement is generally file or jar if ("file".equals(url.getProtocol())) { File[] files = new File(url.getPath() + packagePath) .listFiles(file -> file.getName().endsWith(".class") || file.isDirectory()); for (File file : files) { // Folder, recursive traversal, omitted here if (file.isDirectory()) { continue; } // Output class name classNameList.add(file.getName().replace(".class", "")); } } else if ("jar".equals(url.getProtocol())) { // Get jar package JarFile jarFile = ((JarURLConnection) url.openConnection()).getJarFile(); // Get all the files under the jar package Enumeration<JarEntry> entries = jarFile.entries(); // Filter files by package name while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); if (entry.getName().startsWith(packagePath) || entry.getName().endsWith(".class")) { String name = entry.getName().replace(packagePath, "").replace(".class", ""); // The folder under the package is not traversed here if (name.contains("/")) { continue; } classNameList.add(name); } } } return classNameList; } }
jpa
JPA (Java Persistence API) itself is not a tool or framework; Instead, it defines a set of concepts that can be implemented by any tool or framework. JPA's object relational mapping (ORM) model was originally based on Hibernate.
ORM (Object Relational Mapping) object relational mapping is to solve the problem of mismatch between database data types and programming language data types. ORM can automatically persist the data in the programming language to the database through the mapping relationship between the two data types. Among them, the current mainstream frameworks are Hibernate and MyBatis.
Data type mapping, for example
MySql | Java |
---|---|
bigint | Long |
int | Integer |
datetime | Date |
wait
Annotation
- Table: tag classes are DAO (Data Access Objects) database storage objects
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Table { String value() default ""; }
- Field: the database field corresponding to the tag attribute
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Field { // Database field name String value() default ""; // Data formatting, such as formatting time String format() default ""; }
- Key: mark the attribute as the primary key
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Key { // Database field name String value() default ""; // Is it self increasing boolean autoInc() default true; }
POJO class
- User: persistent database class
@Data @AllArgsConstructor @NoArgsConstructor @Table("t_user") public class User { @Key("user_id") private Long id; @Field("user_name") private String name; // @Field("user_age") private String age; @Field("user_sex") private String sex; }
Persistent Mapper
- BaseMapper: connect to the database and get annotations for sql generation
public class BaseMapper<T> extends BaseLogger { private static BasicDataSource datasource; static { // Connect to database datasource = new BasicDataSource(); datasource.setDriverClassName("com.mysql.cj.jdbc.Driver"); datasource.setUrl("jdbc:mysql://xxx:3306/java_advance"); datasource.setUsername("xxx"); datasource.setPassword("xxx"); } // Get JDBC template private JdbcTemplate jdbcTemplate = new JdbcTemplate(datasource); // Objects for DAO operations private Class<T> beanClass; /** * constructor * Complete the acquisition of actual type parameters during initialization. For example, insert BaseDao < User > into User, and beanClass is User class */ public BaseMapper() { // Gets the real class name of the generic class in the current class beanClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]; } public void add(T bean) { // Is the current 'POJO' marked as a 'DAO' class Table annoTable = beanClass.getAnnotation(Table.class); if (annoTable == null) { throw new NoTableClassException("this class without table annotation"); } // Get all the fields of the User object Field[] declaredFields = beanClass.getDeclaredFields(); // Field part List<String> fieldStr = new ArrayList<>(); List<String> valueStr = new ArrayList<>(); for (int i = 0; i < declaredFields.length; i++) { Field field = declaredFields[i]; // Exclude auto increment fields and fields without key and field annotations Key annoKey = field.getAnnotation(Key.class); com.mochen.advance.annotation.jpa.anno.Field annoField = field.getAnnotation(com.mochen.advance.annotation.jpa.anno.Field.class); if ((annoKey != null && annoKey.autoInc()) || (annoKey == null && annoField == null)) continue; // If the name is not manually set in the annotation, the field name is calculated directly String columnName = ""; if (annoKey != null && StrUtil.isNotBlank(annoKey.value())){ columnName = annoKey.value(); } if (annoField != null && StrUtil.isNotBlank(annoField.value())){ columnName = annoField.value(); } if (StrUtil.isBlank(columnName)){ columnName = getColumnName(field.getName()); } fieldStr.add(columnName); valueStr.add("?"); } // In the table name part, you need to convert the list into a string, such as [1,2,3], and convert square brackets into parentheses StringBuilder sql = new StringBuilder() .append("insert into ") .append(StrUtil.isBlank(annoTable.value()) ? getColumnName(beanClass.getSimpleName()) : annoTable.value()) .append(" " + fieldStr.toString().replaceAll("\\[", "(").replaceAll("\\]", ")")) .append(" values ") .append(valueStr.toString().replaceAll("\\[", "(").replaceAll("\\]", ")")); // Get the value of the bean field (the record to insert) ArrayList<Object> paramList = new ArrayList<>(); try { for (Field declaredField : declaredFields) { // Filter out the key s that are self incrementing fields Key annoKey = declaredField.getAnnotation(Key.class); if (annoKey != null && annoKey.autoInc()) continue; com.mochen.advance.annotation.jpa.anno.Field annoField = declaredField.getAnnotation(com.mochen.advance.annotation.jpa.anno.Field.class); if (annoField == null && annoKey == null) continue; declaredField.setAccessible(true); Object o = declaredField.get(bean); paramList.add(o); } } catch (IllegalAccessException e) { e.printStackTrace(); } int size = paramList.size(); Object[] params = paramList.toArray(new Object[size]); // Pass in the sql statement template and the parameters required by the template, and insert the User int num = jdbcTemplate.update(sql.toString(), params); log.info(sql.toString()); log.info(Arrays.toString(params)); } /** * Get the field name and change the hump naming method to underline naming, for example: classname -- > class_ name */ private static String getColumnName(String className) { String tableName = className; tableName = tableName.replaceAll("[A-Z]", "_$0").toLowerCase(); if (tableName.charAt(0) == '_') { return tableName.substring(1); } return tableName; } }
- UserMapper: Specifies the Mapper of the POJO
public class UserMapper extends BaseMapper<User> {}
Custom exception
- NoTableClassException: if the specified class is not marked with @ Table annotation, this exception will be thrown
public class NoTableClassException extends RuntimeException{ public NoTableClassException(String msg){ super(msg); } }
Test class
- Test
public class Test { public static void main(String[] args) { User user = new User(1L, "lxc", "13", "1"); UserMapper userMapper = new UserMapper(); // Call a method inherited from BaseMapper userMapper.add(user); } }