Reflection and annotation

Posted by cheese on Thu, 17 Feb 2022 21:31:48 +0100

Class loading concept and class loading timing

Class loading concept

When the class file is loaded into the jvm, a class object will be created accordingly; It is divided into three steps: loading, connecting and initializing

load:

  • Load the class file into the memory area and generate a class object accordingly

connect:

  • Verify: verify whether the structure of the class is correct
  • Preparation: initializing static members
  • Parsing: convert bytes into references (objects, variables, methods) that the jvm can execute

initialization:

  • Initializes a member variable in an object

Class loading timing

1. Create an instance of a class object

2. Access static member variables of the class or assign values to static variables

3. Calling a static method of a class

4. Use reflection to force the creation of Java. Net corresponding to a class or interface Lang.class object

Class.forName(“com.mysql.jdbc.Driver”); The runtime object of the driver class is created

5. Initializes a subclass of a class

Initialize the subclass Son class of Fahter class: the Father class will also be loaded
The Son class will use the member variables in the Father class. The member variables in the Father class need to be initialized, the Father needs to be loaded into memory, and the subclass needs to call the construction method of the parent class

6. Directly use Java Exe command to run a main class

Class loader

Concept: load the class file into memory and generate the corresponding class object

classification

Root class loader: Bootstrap ClassLoader

  • Load the core classes in java, mainly C: \ program files \ java \ jdk1 8.0_ 101 \ JRE \ lib \ rt.jar

Extension ClassLoader: Extension ClassLoader

  • Load the extension classes in java, mainly C: \ program files \ java \ jdk1 8.0_ 101 \ JRE \ lib \ ext classes in all jar packages

System class loader: Sysetm ClassLoader

  • Load custom classes written by developers and classes in some third-party jar packages

Reflection overview

Quasi reflective mechanism

Concept: through the Class object of the Class, dynamically call the properties and methods in the Class when the program is running; In other words, the object manipulated by reflection is the Class running in memory. You can clearly know the relevant information in the Class through reflection

How to get class object

Full class name = package name Class name

Class.forName("full class name")

Compilation period

Class name class

Runtime

Object name getClass()

// Three ways to get Class objects
public static void main(String[] args) throws Exception {
    // If you only know the package name + Class name, you can get the Class object corresponding to the User
    Class<?> clazz01 = Class.forName("com.atmin.bean.User");
    // At compile time, there is already a Class object
    Class clazz02 = User.class;
    // Runtime
    User user = new User();
    Class<? extends User> clazz03 = user.getClass();
    // What are the characteristics of clazz01, clazz02 and clazz03?
    // All are the same object, and there is only one object for the Class object (runtime object, bytecode file object) of the same Class
    System.out.println(clazz01 == clazz02);// true
    System.out.println(clazz02 == clazz03);// true
    System.out.println(clazz01 == clazz03);// true
}

Reflection combined with factory mode

// Apple
// Poor reusability leads to high coupling and poor maintainability. Inheritance is needed
public class Apple extends Fruit {
    // Unique attributes of apple
    private int height;// height
    public Apple() {
    }
    public Apple(String fruitName, int height) {
        super(fruitName);
        this.height = height;
    }
}
// Banana
public class Banana extends Fruit {
    private int width;
    public Banana() {
    }
    public Banana(String fruitName, int width) {
        super(fruitName);
        this.width = width;
    }
}
// Fruits: improved maintainability
public class Fruit {
    private String fruitName;

    public Fruit() {
    }

    public Fruit(String fruitName) {
        this.fruitName = fruitName;
    }

    public String getFruitName() {
        return fruitName;
    }

    public void setFruitName(String fruitName) {
        this.fruitName = fruitName;
    }

    @Override
    public String toString() {
        return "Fruit{" +
                "fruitName='" + fruitName + '\'' +
                '}';
    }
}

Primitive means

Direct new object

public static void main(String[] args) {
    // Get an apple
    Apple apple = new Apple("Red Fuji", 10);
    // Get a banana
    Banana banana = new Banana("Golden banana", 10);
    // Coupling is too high
    // The coupling between Apple class and other components is too high
}

Use inheritance

Solve the code reusability of Apple class and Banana class

Banana class, Apple inherits Fruit class

Use factory mode

Solve the problem of high coupling between Banana, Apple and other modules

public class FruitFactory1 {
    public static Apple getApple() {
        return new Apple("Red Fuji", 10);
    }

    public static Banana getBanana() {
        return new Banana("East Asian banana", 10);
    }
}
public static void main(String[] args) {
    //Get an apple
    Apple apple = FruitFactory1.getApple();
    // Get a banana
    Banana banana = FruitFactory1.getBanana();
}

Combined polymorphism: more scalable

public class FruitFactory2 {
    // The reference object points to the parent and child classes
    public static Fruit getFruit(String fruitName){
        if ("apple".equals(fruitName)) {
            return new Apple("Red Fuji", 10);
        } else if ("banana".equals(fruitName)) {
            return new Banana("South Asian banana", 10);
        }
        return null;
    }
}
public static void main(String[] args) {
    // Get an apple
    Apple apple = (Apple) FruitFactory2.getFruit("apple");
    // Get a banana
    Apple banana = (Apple) FruitFactory2.getFruit("banana");
}

Use reflection mechanism

Solve the problem that there are too many if... else codes in the getFruit method of the factory

public class FruitFactory3 {
    // Create objects by reflection
    public static Fruit getFruit(String fruitName) throws Exception {
        // newInstance(): equivalent to creating an object
        return (Fruit) Class.forName(fruitName).newInstance();
    }
}
public static void main(String[] args) throws Exception {
    // Get apple
    Fruit fruit1 = FruitFactory3.getFruit("com.atmin.bean.Apple");
    System.out.println(fruit1);// Fruit{fruitName='null'}
    //Get bananas
    Fruit fruit2 = FruitFactory3.getFruit("com.atmin.bean.Banana");
    System.out.println(fruit2);// Fruit{fruitName='null'}
}

Reflection common API

Reflection operation constructor

// Reflection operation construction method
public static void main(String[] args) throws Exception {
    // Get the Class object corresponding to the User Class
    Class<?> clazz = Class.forName("com.atmin.bean.User");
    // Get parameterless constructor object
    Constructor<?> c1 = clazz.getConstructor();
    // Create a User class object with no parameters
    // newInstance(): create the corresponding class object according to the construction method object
    Object obj1 = c1.newInstance();
    System.out.println(obj1);// User{id=null, username='null', password='null'}

    // Get the parameterized construction method object corresponding to the User class. You need to pass in the class object of parameter type
    Constructor<?> c2 = clazz.getConstructor(Integer.class, String.class, String.class);
    // Creating User objects with parameters
    Object obj2 = c2.newInstance(1, "Zhang San", "root");
    System.out.println(obj2);// User{id=1, username = 'Zhang San', password='root '}
    // The above operations are all public construction methods

    // Use private to decorate the parameter structure. The current structure can only be used in this class
    // getDeclaredConstructor: any type of constructor can get it
    Constructor<?> c3 = clazz.getDeclaredConstructor(String.class, String.class);
    // Violent reflection allows private constructor objects to be accessed externally
    c3.setAccessible(true);// This line of code needs to be added. If it is not added, an illegal access exception will be reported and private members cannot be accessed
    Object obj3 = c3.newInstance("Zhang San", "20000101");
    System.out.println(obj3);
}

Reflection operation member variable

// Reflection operation member variable
public static void main(String[] args) throws Exception {
    // Gets the Class object of the User Class
    Class<User> clazz = User.class;
    // Get the User object through clazz
    User user = clazz.newInstance();
    // Operate the member variable decorated with public to return the field object corresponding to the id attribute
    Field idField = clazz.getField("id");

    // Set the value of the member variable
    // Parameter 1: obj: object to be set
    // Parameter 2: value: the value to be set
    // Set the id attribute of the user object to 250
    // Member variable belongs to object (User)
    idField.set(user, 250);// set up
    System.out.println(user);// User{id=250, username='null', password='null'}
    // Get the value of the member variable and pass it to an object
    Object idValue = idField.get(user);// obtain
    System.out.println(idValue);// 250

    // Operate on non public decorated member variables
    // getDeclaredField can get private
    Field usernameField = clazz.getDeclaredField("username");
    // Violent reflex
    usernameField.setAccessible(true);
    usernameField.set(user, "Hyk ");// set up
    System.out.println(user);// User{id=250, username = 'kunkun', password='null '}
    Object usernameValue = usernameField.get(user);// obtain
    System.out.println(usernameValue);// Hyk 
}

Reflection operation member method

// Reflection operation member method
public static void main(String[] args) throws Exception {
    // Gets the Class object of the User Class
    Class<User> clazz = User.class;
    // Get User object
    User user = clazz.newInstance();

    // Gets the member method of the public modifier
    // Parameter 1: method name
    // Parameter 2: Class object of parameter (formal parameter) type required by the method
    // Get the method object corresponding to setPassword
    Method method01 = clazz.getMethod("setPassword", String.class);
    // To use a method object, you need to pass in the specified object and specified parameters
    // Parameter 1: obj: which object is executing the method? Generally, ordinary member methods are executed by objects
    // Parameter 2: args: parameter value required for method execution
    method01.invoke(user,"123456");// Execute the method
    System.out.println(user);// User{id=null, username='null', password='123456'}

    // Member methods that operate on non public modifiers
    Method method02 = clazz.getDeclaredMethod("show");
    method02.setAccessible(true);
    // invoke needs to pass an object here
    // Result the result (return value) returned after the method is executed
    Object result = method02.invoke(user);
    System.out.println(result);// hello world
}

Reflection application scenario

Reflection over generic checking

The scope of generics in java is at compile time, that is, there is no generics at run time

Reflection technology can dynamically call the add method in the List class to add any type of elements to the collection when the program is running

public static void main(String[] args) throws Exception {
    // When creating a List collection object, you can only store integers. Generics specify that the elements in this collection can only be integers
    List<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(3);
    System.out.println(list);// [1, 2, 3]

    // Generics are only valid at compile time. There are no generics at run time, so they can be added to the collection dynamically at run time
    // Reflection over generic checking
    // Reflection can dynamically call the add method in the List to add elements when the program is running
    Class<? extends List> clazz = list.getClass();
    // Get the add method
    // The essence of generics is Object
    Method add = clazz.getDeclaredMethod("add", Object.class);
    // Violent reflex
    add.setAccessible(true);
    Object result = add.invoke(list, "hello , generic type !");
    System.out.println(list);// [1, 2, 3, hello , generic type !]
    System.out.println(result);// true
}

General method of reflection

Requirement: set the specified value to the specified field of the specified object

Generally, the fields in java entity classes are decorated with private. If you want to operate, you need to use violent reflection, which should not be used in development

Code implementation:

1. Gets the name of the set method corresponding to the specified field

2. Gets the set method object and the specified field type

3. Execute set method

public static void main(String[] args) throws Exception {
    User user = new User();
    // Field names need to be exactly the same
    setValue(user, "id", 123);
    System.out.println(user);// User{id=123, username='null', password='null'}
}

// Requirement: set the specified value for the specified attribute of the specified object
// obj: specify object, fieldName: specify attribute, value: specify value
// Spring framework: This is what the bottom layer does for the injection of member variable values
public static void setValue(Object obj, String fieldName, Object value) throws Exception {
    Class<?> clazz = obj.getClass();
    // Method name specification (meet hump): if there is only one word, all letters are lowercase. If there are multiple words, start with the second word and capitalize the first letter
    // Variable name specification (meet hump): if there is only one word, all letters are lowercase. If there are multiple words, start with the second word and capitalize the first letter
    // username -> setUsername
    // Dynamically obtain the corresponding set method name according to the attribute name
    // Generally, violent reflection operation set method is not recommended. Obtain the corresponding set method name according to the attribute name
    // String methodName = "set" + "U" + "sername";
    String methodName = "set" + fieldName.substring(0,1).toUpperCase() + fieldName.substring(1);
    // Get the field, because the field type is required to determine the parameter type of the set method
    Field field = clazz.getDeclaredField(fieldName);
    // Gets the data type of the field
    Class<?> fieldType = field.getType();
    // Get set method object
    // Take the set method object and pass not only the method name, but also the operation type required when calling the method
    Method method = clazz.getMethod(methodName, fieldType);
    // Execute set method
    method.invoke(obj, value);
}

Reflection binding profile

# bean01: unique identification of the object
# com.atmin.bean.User: the full class name corresponding to the object
# Create an object based on this configuration
bean01=com.atmin.bean.User
bean02=com.atmin.bean.Banana
public static void main(String[] args) throws Exception {
    // Requirement: write bean Properties, the unique identification of the configuration object and the full class name of the object. An object is created according to this configuration
    Properties properties = new Properties();
    // Add bean The data in properties is stored in the input stream inputStream
    // The configuration file read in this way can only be placed under src
    InputStream inputStream = Demo11.class.getClassLoader().getResourceAsStream("bean.properties");
    // Add bean The data in Properties is bound to the Properties object
    properties.load(inputStream);// Pass in an input stream
    // Get full class name
    String className = properties.getProperty("bean01");
    // Created an object based on the full class name above
    Object obj = Class.forName(className).newInstance();
    System.out.println(obj);// User{id=null, username='null', password='null'}

    String className2 = properties.getProperty("bean02");
    Object obj2 = Class.forName(className2).newInstance();
    System.out.println(obj2);// Fruit{fruitName='null'}
}

Framework principle [overview]

public static void main(String[] args) throws Exception {
    // Get apple
    Fruit fruit1 = FruitFactory3.getFruit("com.atmin.bean.Apple");
    System.out.println(fruit1);
    //Get bananas
    Fruit fruit2 = FruitFactory3.getFruit("com.atmin.bean.Banana");
    System.out.println(fruit2);

    /*
    Existing problems:
    com.atmin.bean.Apple Corresponding object
    Is it appropriate to write the above "com.atmin.bean.Apple" string into java code?
    Inappropriate. If the full class name is changed, you must modify the java source code and redeploy the project
    "com.atmin.bean.Apple"The coupling between strings and java programs is very high
    "com.atmin.bean.Apple"The string cannot be placed in java code, but should be placed in the configuration file
    Only modifying the configuration file does not require redeployment of the project
    This is why "com.mysql.jdbc.Driver" should be configured to JDBC In properties
    Decoupling: reduce coupling
    Summary: configuration file + factory schema + reflection + annotation + xml parsing are the core principles of spring framework
     */
}

Static agent design pattern

Concept: enhance the function of the proxy class

step

  • The custom proxy class implements the same interface as the proxy class
  • Declare the object of the proxy class in the proxy class
  • Use the method called by the proxy class in the method of the proxy class
public interface UserDao {
    void addUser();
    void deleteUser();
    void updateUser();
    void selectUser();
}

Proxy class [enhanced class, target object]

/**
 * Proxied class (enhanced class)
 * Main functions:
 *      Add user
 *      delete user
 *      Modify user
 *      Query user
 * Secondary functions: permission verification and logging do not need to be placed in this class, otherwise it does not meet the single principle
 */
public class UserDaoImpl implements UserDao {
    @Override
    public void addUser() {
        // System.out.println("permission check");
        System.out.println("UserDaoImpl addUser");
        // System.out.println("logging");
    }

    @Override
    public void deleteUser() {
        System.out.println("UserDaoImpl deleteUser");
    }

    @Override
    public void updateUser() {
        System.out.println("UserDaoImpl updateUser");
    }

    @Override
    public void selectUser() {
        System.out.println("UserDaoImpl selectUser");
    }
}

Agent [enhanced]

// 1. Customize a proxy class (enhanced class) to implement the same interface as the proxy class (enhanced class)
/**
 *  Proxy class (enhanced class)
 *      Complete non essential functions: permission verification: logging
 */
public class UserDaoImplProxy implements UserDao {
    // 2. Declare the reference of the proxy class in the proxy class
    // The proxy class is a private class, which is created in the proxy class, which means that the access permission of the outside world can be controlled
    // If you want to be accessed by the outside world, provide get and set methods
    // If you don't want to be accessed by the outside world, delete the set and get methods, which means you can control them
    private UserDao userDao;

    /*
    public UserDao getUserDao() {
        return userDao;
    }

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
     */

    // Initialization by constructor
    public UserDaoImplProxy() {
        userDao = new UserDaoImpl();
    }

    @Override
    public void addUser() {
        // 3. Use the method called by the proxy class in the method of the proxy class
        System.out.println("Permission verification");
        userDao.addUser();
        System.out.println("Logging");
    }

    @Override
    public void deleteUser() {
        userDao.deleteUser();
    }

    @Override
    public void updateUser() {
        userDao.updateUser();
    }

    @Override
    public void selectUser() {
        userDao.selectUser();
    }
}

test

/**
     * 1,All roles in the proxy mode (proxy object, target object, interface of target object) are determined at compile time
     * 2,Purpose of static proxy: control the access rights of real objects, and control the use rights of real objects through proxy objects
     * 3,Avoid creating large objects: by using a proxy small object to represent a real large object, you can reduce the consumption of system resources, optimize the system and improve the running speed
     * 4,Enhance the function of the real object. This is relatively simple. Through the proxy, you can add additional functions before and after calling the method of the real object
     */
public static void main(String[] args) {
    // Original means:
    // UserDao userDao = new UserDaoImpl();
    // userDao.addUser();
    // userDao.deleteUser();
    // userDao.updateUser();
    // userDao.selectUser();

    // Use static proxy:
    // Implementation steps (three steps, rote learning)
    // The proxy class is also called enhanced class, and the proxy class is also called enhanced class
    // 1. Customize a proxy class (enhanced class) to implement the same interface as the proxy class (enhanced class)
    // 2. Declare the reference (object) of the proxy class in the proxy class
    // 3. Use the method called by the proxy class in the method of the proxy class
    UserDaoImplProxy userDaoImplProxy = new UserDaoImplProxy();
    userDaoImplProxy.addUser();
    // There are still problems: the other three methods do not need to be enhanced and do not need to be written into the proxy class. This is the disadvantage of the static proxy mode
}

characteristic

Disadvantages: all methods of the proxied class interface must be rewritten (including methods that do not need to be enhanced, which increases the coupling)

Function: enhance the function of the proxy class

Features: it can control the proxy class object

Decorator design pattern

step

  • Define the decorated class (enhanced class) to implement the same interface as the decorated class (enhanced class)
  • Declare a reference to the decorated class in the decorated class
  • In the method of the decoration class, the original method is called with the decoration
public interface UserDao {
    void addUser();
    void deleteUser();
    void updateUser();
    void selectUser();
}

Decorated [enhanced]

public class UserDaoImpl implements UserDao {
    @Override
    public void addUser() {
        System.out.println("UserDaoImpl addUser");
    }

    @Override
    public void deleteUser() {
        System.out.println("UserDaoImpl deleteUser");
    }

    @Override
    public void updateUser() {
        System.out.println("UserDaoImpl updateUser");
    }

    @Override
    public void selectUser() {
        System.out.println("UserDaoImpl selectUser");
    }
}

Decoration [enhancement]

// Decoration (enhancement)
// 1. Define the decorated class (enhanced class) to implement the same interface as the decorated class (enhanced class)
public class UserDaoWrapper implements UserDao {
    // a key:
    // 2. Declare a reference to the decorated class (not an object) in the decorated class
    // Now the proxy object is not created by itself, but passed in externally
    // The decorated class cannot be controlled, because the decorated class is created externally and has been exposed to the outside and cannot be controlled. This is the difference from the static agent design pattern
    private UserDao userDao;

    // No participation doesn't make much sense
    public UserDaoWrapper() {
    }

    // Passed in by reference of decoration class
    public UserDaoWrapper(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void addUser() {
        System.out.println("Permission verification");
        userDao.addUser();
        System.out.println("Logging");
    }

    @Override
    public void deleteUser() {
        userDao.deleteUser();
    }

    @Override
    public void updateUser() {
        userDao.updateUser();
    }

    @Override
    public void selectUser() {
        userDao.selectUser();
    }
}

test

/**
 * Decorator design pattern: it is also used to enhance methods in a class without invading the original code
 * Development steps:
 *  1,Define the decorated class (enhanced class) to implement the same interface as the decorated class (enhanced class)
 *  2,Declare a reference to the decorated class (not an object) in the decorated class
 *  3,In the method of the decoration class, the original method is called with the decoration
*/
public static void main(String[] args) {
    // To create a decorative class object, you need to pass in a decorated class
    UserDaoWrapper userDaoWrapper = new UserDaoWrapper(new UserDaoImpl());
    userDaoWrapper.addUser();
    userDaoWrapper.updateUser();
}

characteristic

Disadvantages of rewriting all methods in a single responsibility

Function: enhance a function without invading the source code of the decorated class

Features: cannot control the decorated objects

Dynamic Proxy design pattern [Proxy]

Concept: interface based method enhancement

Method 1: define a class to implement InvocationHandler interface [enhance addUser method]

// MyInvocationHandler is an enhanced proxy class, not a proxy class. The enhanced code is in the invoke method [only internal enhancement]
class MyInvocationHandler implements InvocationHandler {
    // Declare a reference to a proxied class
    private UserDao userDao;

    // Pass in the proxied class object through parameters
    public MyInvocationHandler(UserDao userDao) {
        this.userDao = userDao;
    }

    // Processing enhancements through the invoke method
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// Processing enhancement
        // The first parameter doesn't matter
        // Method: the original method of the proxy class
        // Object[] args: the actual parameter of the method of the proxied class
        // Execute the original method in the proxied class
        String methodName = method.getName();
        Object returnValue = null;
        if ("addUser".equals(methodName)) {
            // Only addUser needs to be enhanced
            System.out.println("Permission verification");
            returnValue = method.invoke(userDao, args);// Execute the original method in the proxied class
            System.out.println("Logging");
        } else {
            // Other methods not enhanced
            method.invoke(userDao, args);// Execute the original method in the proxied class
        }
        return returnValue;
    }
}

/**
* Test:
* Dynamic proxy mode: it mainly solves the disadvantage of rewriting all methods in the interface. Dynamic proxy mode is also used to enhance methods
* Proxy newProxyInstance() method in class
* The steps of dynamic proxy mode are also fixed
*/
public static void main(String[] args) {
    // Classloader: the class loader corresponding to the proxy class
    // Class<?> [] interfaces: all interfaces implemented by the proxy class
    // InvocationHandler h: a processor used to enhance the proxy class
    UserDao userDao = new UserDaoImpl();// Proxied class object
    // The returned Object must be an interface after forced conversion. If it is not an interface, an error will be reported
    UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(
        userDao.getClass().getClassLoader(),
        userDao.getClass().getInterfaces(),
        new MyInvocationHandler(userDao));
    userDaoProxy.addUser();
    userDaoProxy.deleteUser();
    userDaoProxy.updateUser();
    userDaoProxy.selectUser();
    // According to the above code results, it is found that only the addUser method has been enhanced
}

Method 2: use anonymous inner class object of InvocationHandler interface [enhanced deleteUser method]

// Dynamic agent design pattern
// Anonymous inner class object
public static void main(String[] args) {
    UserDao userDao = new UserDaoImpl();
    // If anonymous inner class object is used, the returned type must be interface
    UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(
        userDao.getClass().getClassLoader(),
        userDao.getClass().getInterfaces(),
        new InvocationHandler() {// Processing enhanced objects
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                String methodName = method.getName();
                Object returnValue = null;
                if ("deleteUser".equals(methodName)) {
                    System.out.println("Permission verification");
                    returnValue = method.invoke(userDao, args);
                    System.out.println("Logging");
                } else {
                    returnValue = method.invoke(userDao, args);
                }
                return returnValue;
            }
        });
    // Only the deletion method is enhanced
    userDaoProxy.deleteUser();
    userDaoProxy.addUser();
}

The advantage of dynamic proxy: you don't need to rewrite all the methods of the interface

Custom database connection pool

concept

  • Without using the connection pool, if 100 users want to operate the database, they need to create 100 connection objects. After operating the database, they need to destroy 100 connection objects. Creating and destroying connections is a waste of system performance

  • If the connection pool is used, the connection is created only when the connection pool is initialized. When users want to operate the database, they only need to take out the created connection object from the connection pool. After operating the database, there is no need to destroy the connection, but just return the connection object to the connection pool

Summary: connection pool improves the performance of operating database

Custom connection pool Basic Edition

Code implementation:

1. During initialization, some connection objects are created and stored in the LinkedList collection

2. Remove the connection from the connection pool

3. Return the connection to the connection pool

Configuration file (placed in src directory)
driverClass=com.mysql.jdbc.Driver
jdbcUrl=jdbc:mysql://localhost:3306/day57
user=root
password=123456
JDBC tool class
/**
 * JDBC Tools:
 *    Load driver
 *    Get connection
 *    Release resources
 */
public class JDBCUtils {
    private static final String DRIVERCLASS;
    private static final String JDBCURL;
    private static final String USER;
    private static final String PASSWORD;

    static {
        Properties properties = new Properties();
        InputStream inputStream = JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
        try {
            properties.load(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
        DRIVERCLASS = properties.getProperty("driverClass");
        JDBCURL = properties.getProperty("jdbcUrl");
        USER = properties.getProperty("user");
        PASSWORD = properties.getProperty("password");
    }

    // Load driver
    public static void loadDriver() throws Exception {
        Class.forName(DRIVERCLASS);
    }

    // Get connection
    public static Connection getConnection() throws Exception {
        loadDriver();
        return DriverManager.getConnection(JDBCURL, USER, PASSWORD);
    }

    // Free resources: dedicated to processing queries
    public static void release(Connection connection, Statement statement, ResultSet resultSet) {
        if (null != connection) {
            try {
                // Enhanced close method implemented: the function is to return the connection
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            connection = null;
        }

        if (null != statement) {
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            statement = null;
        }

        if (null != resultSet) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            resultSet = null;
        }
    }

    // Release resources: specifically deal with additions, deletions and modifications
    public static void release(Connection connection , Statement statement) {
        release(connection, statement, null);
    }
}
Database connection pool
public class MyDataSource {
    int initPoolSize = 3;// Number of initial connections in the connection pool

    LinkedList<Connection> connections = new LinkedList<>();

    // When the connection pool is initialized, some connections are created and stored
    public MyDataSource() {
        // When can I listen to object initialization - > constructor
        // Initialization of connection pool
        createConnections();
    }

    public void createConnections() {
        for (int i = 0 ; i <= initPoolSize ; i++) {
            try {
                Connection connection = JDBCUtils.getConnection();
                // Store the created connection object
                /**
                 * Storage connection: array, set (single column, double column)
                 * Single column set: ArrayList, LinkedList, HashSet, TreeSet
                 * ArrayList: Array, fast query and modification, slow addition and deletion
                 * LinkedList: Linked list, slow query and modification, fast addition and deletion
                 * HashSet: duplicate removal
                 * TreeSet: sort
                 * Whether to use ArrayList or LinkedList depends on the characteristics of the connection pool. LinkedList should be used to remove and add connections
                 */
                connections.add(connection);// Add one when you create one
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    // Remove the connection
    public Connection getConnection() {
        // There are enough connections in the connection pool
        if(connections.size() == 0 || connections.isEmpty()) {
            // There are not enough connections in the connection pool. Some new connections are stored in the connection pool and cannot be used
            createConnections();
        }
        // Remove the connection object marked 0 and return it
        Connection connection = connections.remove(0);
        return connection;
    }

    // Return connection
    public void addBack(Connection connection) {
        connections.add(connection);
    }
}
test
public static void main(String[] args) {
    MyDataSource myDataSource = new MyDataSource();
    Connection connection = myDataSource.getConnection();
    PreparedStatement statement = null;
    ResultSet resultSet = null;
    try {
        statement = connection.prepareStatement("select * from tb_user where id = ?");
        statement.setInt(1, 2);
        resultSet = statement.executeQuery();
        User user = null;
        if(resultSet.next()) {
            int id = resultSet.getInt("id");
            String username = resultSet.getString("username");
            String password = resultSet.getString("password");
            user = new User(id, username, password);
        }
        System.out.println(user);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        // You can't close the connection. You should return it
        JDBCUtils.release(null, statement, resultSet);
        // Return connection
        myDataSource.addBack(connection);
        // Existing problems:
        // The addBack method is very weak because it was written by an individual
        // Others remember the close method in Connection
        // Can I make a return connection through the close method
    }
}

Existing problems: I have defined the method addBack to return the connection, which is difficult to maintain

Note: idea2020 When using lombok plug-in in version 3, in addition to importing dependencies and opening annotation development, the following configuration is also required: - djps track. ap. dependencies=false

Or replace it with lombok-1.18.16 Jar version

Customized connection pool optimized version [decorator design mode]

Solutions:

  • General developers can remember the close method in the Connection class
  • Change the function of the close method to return the connection

Code implementation:

1. When the connection pool is initialized, some connections are created and stored in the LinkedList

2. Add the decorator design pattern and change the close method in the ordinary Connection object Connection to return the Connection

To solve the problem of undermining the principle of single responsibility, intermediate classes are used

3. Get the connection, use decorator design mode, and return the enhanced connection object

4. Return the connected addBack to delete it

Existing problems:

You need to override all methods in the interface

The intermediate class ConnectionWrapper focuses on the call of non close methods
/**
 * Access modifier review:
 *  public: Anywhere
 *  protected: Only in child parent relationships
 *  Default: same package
 *  private: This category
 */
// Intermediate class: handle methods other than close, and use ordinary Connection to call once
// The drawback of decorator design pattern is that all methods need to be rewritten, so each method is called once
public class ConnectionWrapper implements Connection {
    // protected: can only be accessed in child and parent classes
    protected Connection connection;// You have to have an ordinary connection object

    public ConnectionWrapper() {
    }

    public ConnectionWrapper(Connection connection) {
        this.connection = connection;
    }

    @Override
    public Statement createStatement() throws SQLException {
        return connection.createStatement();
    }

    @Override
    public PreparedStatement prepareStatement(String sql) throws SQLException {
        // Call the prepareStatement method with an ordinary connection object
        return connection.prepareStatement(sql);
    }

    @Override
    public CallableStatement prepareCall(String sql) throws SQLException {
        return connection.prepareCall(sql);
    }

    @Override
    public String nativeSQL(String sql) throws SQLException {
        return connection.nativeSQL(sql);
    }

    @Override
    public void setAutoCommit(boolean autoCommit) throws SQLException {
        connection.setAutoCommit(autoCommit);
    }

    @Override
    public boolean getAutoCommit() throws SQLException {
        return connection.getAutoCommit();
    }

    @Override
    public void commit() throws SQLException {
        connection.commit();
    }

    @Override
    public void rollback() throws SQLException {
        connection.rollback();
    }

    @Override
    public void close() throws SQLException {
        connection.close();
    }

    @Override
    public boolean isClosed() throws SQLException {
        return connection.isClosed();
    }

    @Override
    public DatabaseMetaData getMetaData() throws SQLException {
        return connection.getMetaData();
    }

    @Override
    public void setReadOnly(boolean readOnly) throws SQLException {
        connection.setReadOnly(readOnly);
    }

    @Override
    public boolean isReadOnly() throws SQLException {
        return connection.isReadOnly();
    }

    @Override
    public void setCatalog(String catalog) throws SQLException {
        connection.setCatalog(catalog);
    }

    @Override
    public String getCatalog() throws SQLException {
        return connection.getCatalog();
    }

    @Override
    public void setTransactionIsolation(int level) throws SQLException {
        connection.setTransactionIsolation(level);
    }

    @Override
    public int getTransactionIsolation() throws SQLException {
        return connection.getTransactionIsolation();
    }

    @Override
    public SQLWarning getWarnings() throws SQLException {
        return connection.getWarnings();
    }

    @Override
    public void clearWarnings() throws SQLException {
        connection.clearWarnings();
    }

    @Override
    public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
        return connection.createStatement(resultSetType,resultSetConcurrency);
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        return connection.prepareStatement(sql,resultSetType,resultSetConcurrency);
    }

    @Override
    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        return connection.prepareCall(sql,resultSetType,resultSetConcurrency);
    }

    @Override
    public Map<String, Class<?>> getTypeMap() throws SQLException {
        return connection.getTypeMap();
    }

    @Override
    public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
        connection.setTypeMap(map);
    }

    @Override
    public void setHoldability(int holdability) throws SQLException {
        connection.setHoldability(holdability);
    }

    @Override
    public int getHoldability() throws SQLException {
        return connection.getHoldability();
    }

    @Override
    public Savepoint setSavepoint() throws SQLException {
        return connection.setSavepoint();
    }

    @Override
    public Savepoint setSavepoint(String name) throws SQLException {
        return connection.setSavepoint(name);
    }

    @Override
    public void rollback(Savepoint savepoint) throws SQLException {
        connection.rollback(savepoint);
    }

    @Override
    public void releaseSavepoint(Savepoint savepoint) throws SQLException {
        connection.releaseSavepoint(savepoint);
    }

    @Override
    public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        return connection.createStatement(resultSetType,resultSetConcurrency,resultSetHoldability);
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        return connection.prepareStatement(sql,resultSetType,resultSetConcurrency,resultSetHoldability);
    }

    @Override
    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        return connection.prepareCall(sql,resultSetType,resultSetConcurrency,resultSetHoldability);
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
        return connection.prepareStatement(sql,autoGeneratedKeys);
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
        return connection.prepareStatement(sql,columnIndexes);
    }

    @Override
    public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
        return connection.prepareStatement(sql,columnNames);
    }

    @Override
    public Clob createClob() throws SQLException {
        return connection.createClob();
    }

    @Override
    public Blob createBlob() throws SQLException {
        return connection.createBlob();
    }

    @Override
    public NClob createNClob() throws SQLException {
        return connection.createNClob();
    }

    @Override
    public SQLXML createSQLXML() throws SQLException {
        return connection.createSQLXML();
    }

    @Override
    public boolean isValid(int timeout) throws SQLException {
        return connection.isValid(timeout);
    }

    @Override
    public void setClientInfo(String name, String value) throws SQLClientInfoException {
        connection.setClientInfo(name,value);
    }

    @Override
    public void setClientInfo(Properties properties) throws SQLClientInfoException {
        connection.setClientInfo(properties);
    }

    @Override
    public String getClientInfo(String name) throws SQLException {
        return connection.getClientInfo(name);
    }

    @Override
    public Properties getClientInfo() throws SQLException {
        return connection.getClientInfo();
    }

    @Override
    public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
        return connection.createArrayOf(typeName,elements);
    }

    @Override
    public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
        return connection.createStruct(typeName,attributes);
    }

    @Override
    public void setSchema(String schema) throws SQLException {
        connection.setSchema(schema);
    }

    @Override
    public String getSchema() throws SQLException {
        return connection.getSchema();
    }

    @Override
    public void abort(Executor executor) throws SQLException {
        connection.abort(executor);
    }

    @Override
    public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
        connection.setNetworkTimeout(executor,milliseconds);
    }

    @Override
    public int getNetworkTimeout() throws SQLException {
        return connection.getNetworkTimeout();
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return connection.unwrap(iface);
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return connection.isWrapperFor(iface);
    }
}
Connection decoration class MyConnectionWrapper
/**
 * Connection decoration class
 * Main function: enhanced close method
 * close There is only one method, while there are many, many other unnecessary functions, which destroys the principle of single responsibility
 * You need to separate the close method from other methods (ordinary connection object calls)
 */
public class MyConnectionWrapper extends ConnectionWrapper {
    // Declaration is referenced by the proxy class
    // There is no need to write after inheritance
    // private Connection connection;

    private List<Connection> connectionList;

    // Regenerate a new constructor according to the parent class, which solves the problem of breaking the single principle
    public MyConnectionWrapper(Connection connection, List<Connection> connectionList) {
        super(connection);
        this.connectionList = connectionList;
    }

    @Override
    public void close() throws SQLException {
        // Enhanced close: return connection
        // Plan connections to Connection pools (collections)
        connectionList.add(connection);
    }
}
MyDataSource
// To customize the optimized version of Connection pool, you need to enhance the close method in Connection
public class MyDataSource {
    int initPoolSize = 3;

    LinkedList<Connection> connections = new LinkedList<>();

    public MyDataSource() {
        createConnections();
    }

    public void createConnections() {
        for (int i = 3; i <= initPoolSize; i++) {
            try {
                Connection connection = JDBCUtils.getConnection();
                connections.add(connection);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    // Remove the connection
    public Connection getConnection() {
        if(connections.size() == 0 || connections.isEmpty()) {
            createConnections();
        }
        // There are enough connections in the connection pool
        // Remove the connection object marked 0 and return it
        Connection connection = connections.remove(0);// Common connection object
        // Key: obtain a connection proxy object (enhanced connection object) according to the ordinary connection object
        MyConnectionWrapper connectionWrapper = new MyConnectionWrapper(connection, connections);
        // An enhanced connection object is returned
        return connectionWrapper;
    }
}
test
public static void main(String[] args) {
    MyDataSource myDataSource = new MyDataSource();
    // Do you want to use a connection object or a proxy object? - > Connection proxy object
    // Under polymorphism, access the close member method: compile and run to the left
    // Connection connection = new MyConnectionWrapper()
    // Compile to the left: see if the Connection has a close method. If so, compile it. If not, an error will be reported
    // Run and look at the right: the close method in MyConnectionWrapper is executed
    Connection connection = myDataSource.getConnection();
    PreparedStatement statement = null;
    ResultSet resultSet = null;
    try {
        // Does the prepareStatement method execute in the ordinary connection object or in the enhanced connection object? - > enhance
        statement = connection.prepareStatement("select * from tb_user where id = ?");
        statement.setInt(1, 3);
        resultSet = statement.executeQuery();
        User user = null;
        if (resultSet.next()) {
            user = new User(resultSet.getInt("id"), resultSet.getString("username"), resultSet.getString("password"));
        }
        System.out.println(user);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        // Return connection
        JDBCUtils.release(connection, statement, resultSet);
    }
}

Custom connection pool ultimate version [dynamic agent design mode]

Code implementation:

1. When the connection pool is initialized, some connections are created and stored in the LinkedList

2. Add a dynamic proxy to change the close method in the ordinary Connection object Connection to return the Connection

3. Get the connection, use the dynamic proxy, and return the enhanced connection object

public class MyDataSource {
    int initPoolSize = 3;

    LinkedList<Connection> connections = new LinkedList<>();

    public MyDataSource() {
        createConnections();
    }

    public void createConnections() {
        for (int i = 0 ; i <= initPoolSize ; i++) {
            try {
                Connection connection = JDBCUtils.getConnection();
                connections.add(connection);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public Connection getConnection() {
        if(connections.size() == 0 || connections.isEmpty()) {
            createConnections();
        }
        Connection connection = connections.remove(0);// Common connection object
        // Originally, an enhanced connection object was returned through decorator design pattern, but now a dynamic proxy is used
        Connection connectionProxy = (Connection) Proxy.newProxyInstance(
                connection.getClass().getClassLoader(),
                connection.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // Processing enhanced close method
                        String methodName = method.getName();
                        Object returnValue = null;
                        if ("close".equals(methodName)) {
                            // If it is a close method, the enhancement is to return the connection
                            returnValue = connections.add(connection);
                        } else {
                            returnValue = method.invoke(connection, args);
                        }
                        return returnValue;
                    }
                });
        return connectionProxy;
    }
}

The test is consistent with the above

  • Conclusion: using dynamic proxy to solve the disadvantages of decorator design pattern, that is, decorator design pattern must rewrite all methods in the interface

annotation

Annotation introduction

Concept: it is a modifier

Features: jdk5 Features introduced after 0 exist in the form of "@ annotation name"

effect:

Trace code dependencies

Perform compile time format check

Replace existing configuration file

JDK built-in annotation

@Overirde

The method specified by the tag is an override method, otherwise an error will be reported

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
@Deprecated

Marking a class, field, or method is an outdated

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
@SuppressWarings
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}

value: the type of warning suppressed by the annotation

unchecked: Unchecked transformations, such as adding elements if the collection does not have a specified type
unused: Unused variables
resource: There are generic types and no type is specified
path: In the classpath, there is a path that does not exist in the original file path
deprecation: Some deprecated classes and methods were used
fallthrough: switch Whether the statement is executed or not break keyword
rawtypes: No generics written,such as: List list = new ArrayList();
all: All types of warnings

Code demonstration

// java built-in annotation
public class Test01 {
    public static void main(String[] args) {
        // @Override: determines whether the method in the subclass is an override method
        // @Deprecated: marks a class, method, or variable as obsolete
        // Outdated can be used, but not recommended
        Son son = new Son();
        son.method01();
        System.out.println(son.num);
    }
}

class Father {
    public void show(){
        System.out.println("Father show");
    }
}

@Deprecated
class Son extends Father{
    // Method does not override method from its superclass
    @Deprecated
    public int num = 1;
    @Override
    public void show(){
        System.out.println("Son show");
    }
    @Deprecated
    public void method01(){
        System.out.println("Son method01");
    }
}
// All suppress all warnings, just remember this one
@SuppressWarnings( "all")
public class Test02 {
    public static void main(String[] args) {
        // @Suppresswarning: suppress warning
        // @SuppressWarnings("unused")
        int num = 1;
        // @SuppressWarnings(value = {"rawtypes","unused"})
        // The warning here is that the collection is best given a generic type
        List list = new ArrayList();
    }
}

Annotation classification

marker annotation

  • There are no attributes in the annotation
  • For example: @ Override, @ Deprecated

Single value annotation

  • There is only one attribute in the annotation
  • For example: @ SuppressWarings
  • When there is only one value attribute in the attribute, value can not be written

Full annotation

  • There are multiple attributes in the annotation

Custom annotation

format
public @interface Annotation name {
    Data type property name 1() default Default 1;
    Data type property name 2() ;
}
Basic use
// Custom annotation MyAnnotation01
public @interface MyAnnotation01 {
    String username() default "root";
    String password() default "root123";
    int age() default 16;
    String value();
}
// Custom annotation test
@MyAnnotation01(value = "hello world" , username = "Zhang San")
public class Test {
    public static void main(String[] args) {
    }
}
matters needing attention

When the value attribute is used alone, "value =" can be omitted

Meta annotation

Meta annotation is understood as annotation of annotation

summary

It acts on user-defined annotations, and specifies the action area and survival strategy of user-defined annotations

Common meta annotations

@Target: Specifies the scope of user-defined annotation

[enumeration class] ElementType[] value():

  • TYPE: class, interface, annotation, enumeration
  • FIELD: member variable
  • METHOD: member METHOD
  • PARAMETER: formal PARAMETER
  • CONSTRUCTOR: CONSTRUCTOR
  • LOCAL_VARIABLE: local variable
  • ANNOTATION_TYP: annotation type
  • PACKAGE: PACKAGE

@Retention: Specifies the survival strategy of custom annotations

[enumeration type] RetentionPolicy value():

  • SOURCE: only survive in the SOURCE code
  • CLASS: live at compile time
  • RUNTIME: survive at RUNTIME
enumeration
Use of meta annotations

@Target

// Common scope of user-defined annotation: class, member method, member variable and formal parameter. These positions can be used by default without setting
// ElementType.TYPE: Class
// ElementType.FIELD: member variable
// ElementType.METHOD: method
// ElementType.PARAMETER: formal parameter
// ElementType.LOCAL_VARIABLE: local variable
@Target(
        value =
            {
                ElementType.TYPE,
                ElementType.FIELD,
                ElementType.METHOD,
                ElementType.PARAMETER,
                ElementType.LOCAL_VARIABLE
            }
        )
public @interface MyAnnotation01 {
    String value() default "hello annotation";
}
// Use of meta annotations
@MyAnnotation01
public class Demo01 {
    @MyAnnotation01
    private int num = 1;

    @MyAnnotation01
    public static void main(@MyAnnotation01 String[] args) {
        @MyAnnotation01
        int num = 1;
    }
}

@Retention

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation01 {
    String value() default "hello annotation";
}

In general, what is the reason for the survival of runte?

  • For annotations to work, they must be used in conjunction with reflection, which is executed at program runtime

Custom annotation @ MyTest

Requirement: in the test class, if the @ MyTest annotation is used on the method, the method will execute

Development steps:

  • Custom annotation @ MyTest
  • Use @ MyTest on the method in test class Test01
  • Make @ MyTest annotation effective
    • Get the Class object corresponding to Test01 Class
    • Get all methods in Test01 class
    • Judge whether there is @ MyTest annotation on the method
      • If so, execute the method
      • If not, don't deal with it

@MyTest comment: @ MyTest's survival strategy must be RUNTIME

// Tag annotation, no need to set attributes
// The default survival strategy is in the source phase and needs to be changed to runtime
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
    String value() default "";
}
public class Test01 {
    // Unit test: junit test
    @Test
    public void test01() {
        System.out.println("test01");
        // Although the main method is not written, the bottom layer is still driven by the main method
        // Works via (reflection + @ Test annotation)
    }

    // Requirement: the method with this annotation will execute
    @MyTest
    public void test02() {
        System.out.println("test02");
    }

    @MyTest
    public void test03() {
        System.out.println("test03");
    }

    public void test04() {
        System.out.println("test04");
    }
}

test

// Make @ MyTest effective
public static void main(String[] args) {
    // Use reflection to scan which methods in Test01 class have @ MyTest annotation
    // If there is @ MyTest annotation, it will be executed
    // If there is no @ MyTest annotation, no processing will be done
    // 1. Get the Class object corresponding to Test01 Class
    Class<Test01> clazz = Test01.class;
    // 2. Get all method objects under Test01 class
    Method[] methods = clazz.getMethods();
    // stream, lambda expression
    // Arrays.stream(methods): convert an array into a stream
    // Calling the forEach method by the stream is equivalent to traversing the array, and the internal traversal is done
    // method: the elements in the array after traversal
    Arrays.stream(methods).forEach(method -> {
        // A method is a single method object
        // 3. Judge whether there is @ MyTest annotation on the method and pass an annotation class
        boolean present = method.isAnnotationPresent(MyTest.class);
        if (present) {
            // If there is @ MyTest annotation on the method, execute the method
            try {
                // Just pass the object
                // Unit test methods cannot have any formal parameters except that the modifier is public
                method.invoke(clazz.newInstance());
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            // There is no @ MyTest annotation on the method and no processing will be done
        }
    });
}

You can't do the same as the official unit test. You can execute it by right clicking the annotation, but the principle is the same

Custom annotation @ JDBCInfo

Requirement: use annotation @ JDBCInfo instead of JDBC Properties configuration file

Development steps:

1. Custom annotation @ JDBCInfo

2. Use @ JDBCInfo on the JDBCUtils tool class

3. In the static code block in the JDBCUtils tool class, obtain the attribute on the @ JDBCInfo annotation and assign a value to the member variable in the JDBCUtils tool class

@Retention(RetentionPolicy.RUNTIME)
public @interface JDBCInfo {
    String driverClass() default "com.mysql.jdbc.Driver";
    String jdbcUrl() default "jdbc:mysql://localhost:3306/nz2002";
    String user() default "root";
    String password() default "123456";
}

Use @ JDBCInfo on the JDBCUtils tool class to reflect and read the content in the annotation @ JDBCInfo

// JDBC tool class, replace the properties configuration file with annotations
@JDBCInfo
public class JDBCUtils {
    private static final String DRIVERCLASS;
    private static final String JDBCURL;
    private static final String USER;
    private static final String PASSWORD;

    static {
        Class<JDBCUtils> clazz = JDBCUtils.class;
        // Judge whether there is this annotation
        boolean present = clazz.isAnnotationPresent(JDBCInfo.class);
        if (present) {
            // There is @ JDBCInfo annotation on the JDBCUtils class. Get the annotation
            JDBCInfo jdbcInfo = clazz.getAnnotation(JDBCInfo.class);
            // Get the attribute values of driverClass, jdbcUrl, user and password from the @ JDBCInfo annotation
            DRIVERCLASS = jdbcInfo.driverClass();
            JDBCURL = jdbcInfo.jdbcUrl();
            USER = jdbcInfo.user();
            PASSWORD = jdbcInfo.password();
        } else {
            // If there is no @ JDBCInfo annotation on the class, take it from the properties file (omitted)
            // No annotation is equivalent to not initializing those constants. Constants must have initial values
            DRIVERCLASS = "";
            JDBCURL = "";
            USER = "";
            PASSWORD = "";
        }
    }

    public static void loadDriver() throws Exception {
        Class.forName(DRIVERCLASS);
    }

    public static Connection getConnection() throws Exception {
        loadDriver();
        return DriverManager.getConnection(JDBCURL, USER, PASSWORD);
    }

    public static void release(Connection connection, Statement statement , ResultSet resultSet) {
        // The judgment here is the problem of null pointer
        if (null != connection) {
            try {
                // Close the connection, not recycle
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            // connection = new Connection();  The connection variable will be recycled in time, and the connection object without reference will also be recycled in time
            // In order for the Connection object to be recycled by the jvm in time, it is set to null here
            // null is equivalent to a meaningless quantity in java
            connection = null;
        }

        if (null != statement) {
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            statement = null;
        }

        if (null != resultSet) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            resultSet = null;
        }
    }

    public static void release(Connection connection, Statement statement) {
        release(connection, statement, null);
    }
}

test

public static void main(String[] args) {
    Connection connection = null;
    PreparedStatement statement = null;
    ResultSet resultSet = null;
    try {
        connection = JDBCUtils.getConnection();
        statement = connection.prepareStatement("select * from tb_user where id = ?");
        statement.setInt(1, 5);
        resultSet = statement.executeQuery();
        if (resultSet.next()) {
            System.out.println(resultSet.getInt("id"));
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        JDBCUtils.release(connection, statement, resultSet);
    }
}

Comprehensive cases of reflection, annotation and design pattern

Based on decorator design pattern

Development steps

1. Custom annotation @ SystemLog

  • className
  • methodName

2. Define a UserDao interface and use the annotation @ SystemLog on this interface

3. Write decorator design pattern

  • Get the Class object of UserDao implementation subclass
  • Gets the Class object of the UserDao interface
  • Gets the method object in the UserDao interface
code implementation

Custom annotation @ SystemLog, annotation survival strategy: RUNTIME

@Retention(RetentionPolicy.RUNTIME)
public @interface SystemLog {
    String className();// Record class name
    String methodName();// Record method name
}

Define a UserDao interface, and use the annotation @ SystemLog in the method of the interface

  • Set the className property and methodName property in @ SystemLog
public interface UserDao {
    void addUser() throws Exception;
    @SystemLog(className = "com.atmin.dao.UserDao", methodName = "deleteUser")
    void deleteUser() throws Exception;
    void updateUser() throws Exception;
}

Implementation class

// Main functions: add user, delete user and modify user
// Auxiliary function: logging
public class UserDaoImpl implements UserDao {
    @Override
    public void addUser() throws Exception {
        System.out.println("UserDaoImpl addUser");
    }
    @Override
    public void deleteUser() throws Exception {
        System.out.println("UserDaoImpl deleteUser");
    }
    @Override
    public void updateUser() throws Exception {
        System.out.println("UserDaoImpl updateUser");
    }
}

Write decorator design pattern

// Decoration
public class UserDaoWrapper implements UserDao {
    private UserDao userDao;

    public UserDaoWrapper(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void addUser() throws Exception {
        userDao.addUser();
        printLog("addUser");
    }

    @Override
    public void deleteUser() throws Exception {
        userDao.deleteUser();
        printLog("deleteUser");
    }

    @Override
    public void updateUser() throws Exception {
        userDao.updateUser();
        printLog("updateUser");
    }

    // Logging
    private void printLog(String runMethodName) throws Exception {
        // Judge whether the corresponding method on the interface has @ SystemLog annotation
        // Gets the Class object of the subclass implemented by the UserDao interface
        Class<? extends UserDao> sonClazz = userDao.getClass();
        // Obtain the Class object of the UserDao interface by implementing the Class object of the subclass
        Class<?>[] interfaces = sonClazz.getInterfaces();
        // Get the Class object of UserDao interface
        Class<?> fatherClazz = interfaces[0];
        // Obtain the corresponding method object in the interface through the method name
        Method method = fatherClazz.getMethod(runMethodName);
        if (null != method) {// Non empty judgment makes the code more robust
            // Judge whether there is @ SystemLog annotation on the method
            boolean present = method.isAnnotationPresent(SystemLog.class);
            if (present) {
                /**
                 * There is @ SystemLog annotation on the method. Print the log
                 * SimpleDateFormat: 
                 * parse(): Parsing: convert time string into time object
                 * format(): Format: converts a time object to a time string
                 */
                // Create a date format object. The parameter specifies the format of the date
                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy year MM month dd day hh:mm:ss");
                // Format object format current time object
                String currentTimeStr = simpleDateFormat.format(new Date());
                SystemLog systemLog = method.getAnnotation(SystemLog.class);
                // Get the class name
                String className = systemLog.className();
                // Get the method name
                String methodName = systemLog.methodName();
                System.out.println(currentTimeStr + "---" + className + "In class" + methodName + "()Method is running");
            } else {
                // There is no @ SystemLog annotation on the method and no processing will be done
            }
        }
    }
}

test

public static void main(String[] args) throws Exception {
    // Requirement: if there is @ SystemLog annotation on the method in the UserDao interface, log it
    // If there is no @ SystemLog annotation, no logging will be performed
    UserDao userDao = new UserDaoImpl();
    UserDaoWrapper userDaoWrapper = new UserDaoWrapper(userDao);
    userDaoWrapper.deleteUser();
    userDaoWrapper.updateUser();
    // Existing problems
    // Each method in the decorator class needs to call the printLog method, which is determined by the shortcomings of the decorator's design pattern
}

Design pattern based on dynamic agent

Development steps

1. Custom annotation @ SystemLog

  • className
  • methodName

2. Define a UserDao interface and use the annotation @ SystemLog on this interface

3. Dynamic agent

  • Gets the method object in the UserDao interface
code implementation

Custom annotation @ SystemLog

@Retention(RetentionPolicy.RUNTIME)
public @interface SystemLog {
    String className();//Record class name
    String methodName();//Record method name
}

Define a UserDao interface and use the annotation @ SystemLog on the interface method

  • Set the className property and methodName property in @ SystemLog
public interface UserDao {
    @SystemLog(className = "com.qfedu.dao.UserDao" , methodName = "addUser")
    void addUser() throws Exception;
    void deleteUser() throws Exception;
    @SystemLog(className = "com.qfedu.dao.UserDao" , methodName = "updateUser")
    void updateUser() throws Exception;
}

Dynamic agent

public static void main(String[] args) throws Exception {
    UserDao userDao = new UserDaoImpl();
    UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(
        userDao.getClass().getClassLoader(),
        userDao.getClass().getInterfaces(),
        // Enhanced processor
        new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // Get the method object in the interface, such as the addUser method in the UserDao interface
                // Previous decoration:
                // Get the subclass of the object first - > omit the implementation of Class
                // Get the Class object of the interface - > now: userdao getClass(). getInterfaces()
                // Then get the method object in the interface - > now: method
                // Parameter method: refers to the method object in the interface. This method is based on userdao getClass(). From getinterfaces()
                Object returnValue = null;
                if (null != method) {
                    boolean present = method.isAnnotationPresent(SystemLog.class);
                    if (present) {
                        // If there is @ SystemLog annotation, execute the original function and print the log
                        returnValue = method.invoke(userDao, args);
                        String currentTimeStr = new SimpleDateFormat("yyyy year MM month dd day hh:mm:ss").format(new Date());
                        // Get notes
                        SystemLog systemLog = method.getAnnotation(SystemLog.class);
                        // Get the class name
                        String className = systemLog.className();
                        // Get the method name
                        String methodName = systemLog.methodName();
                        System.out.println(currentTimeStr + "---" + className + "In class" + methodName + "()Method is running");
                    } else {
                        // If there is no @ SystemLog annotation, execute the original function and do not print the log
                        returnValue = method.invoke(userDao, args);
                    }
                }
                return returnValue;
            }
        });
    userDaoProxy.addUser();
    userDaoProxy.deleteUser();
    userDao.updateUser();
}

Topics: Java reflection