1. Related dependencies

Code file: github.com/lzlz123/spr… (I have here) github.com/tyshawnlee/… (reference)

(reference code, the AOP code has errors, a proxy object is not dependent, so will be submitted to the null pointer exception, solution basically has two, one, will be a proxy object dependency injection, 2, access to the proxy object, by proxy objects reflect the original method, found that the additional can’t through a proxy object appears to be a proxy object, A HashMap was added to save proxy dependency injection.

<dependencies> <! -- Servlet --><dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency><! -- JSP --><dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.2</version>
        <scope>provided</scope>
    </dependency><! JSP standard tag library --><dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
        <scope>runtime</scope>
    </dependency><! -- MySQL --><dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.33</version>
        <scope>runtime</scope>
    </dependency><! -- Database connection pool --><dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-dbcp2</artifactId>
        <version>2.0.1</version>
    </dependency><! --JDBC Tool Class Library --><dependency>
        <groupId>commons-dbutils</groupId>
        <artifactId>commons-dbutils</artifactId>
        <version>1.6</version>
    </dependency><! -- Logging framework --><dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.7</version>
    </dependency><! -- Dynamic proxy dependencies --><dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>2.2.2</version>
    </dependency><! -- Universal tool Kit --><dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.3.2 rainfall distribution on 10-12</version>
    </dependency><! Set toolkits --><dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-collections4</artifactId>
        <version>4.0</version>
    </dependency><! --JSONRely on -- -- ><dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.49</version>
    </dependency>
</dependencies>

Copy the code

2. The work of this paper

  1. Implement a bean container

  2. Implement IOC functionality for containers

The Bean container:

Spring is a container that manages the assembly and life cycle of all beans in an application. The Spring container is actually a Map that stores all instances of beans in an application. The key is the Class object of the bean instance. Spring has two types of containers, BeanFactory and ApplicationContext. The difference between the two is that BeanFactory uses a lazy-loading strategy and does not actually assemble the object until the first time getBean() is called. The ApplicationContext will assemble all the objects at once when the application starts.

IOC: The realization of IOC is as follows:

  • First, there is a configuration file that defines the application’s base package, the Java source path.
  • Read the base package name, and then get all the Class objects in the application through the Class loader and store them in a collection.
  • Get the Class objects of all the beans (Controller and Service) in the application, create instances by reflection, and store them in the Bean container.
  • Iterate over all beans in the Bean container, injecting instances for all attributes annotated with @AutoWired.
  • IOC operations are done when the application starts, so they must be written in a static block of code.

3. Specific function implementation scheme

3.1 Some pre-code

Used to implement dependency injection

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}
Copy the code

For implementing controller

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {

}
Copy the code
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
    /** * Request path **@return* /
    String value(a) default "";

    /** * request method **@return* /
    RequestMethod method(a) default RequestMethod.GET;
}
Copy the code
public enum RequestMethod {
    GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
}

Copy the code
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {
}

Copy the code

3.2 the IOC function

Given a configuration (using the properties file for the time being), you can scan for the classes you want under the package (using annotations, when annotated as classes Service or Controller) and save the CLSS files for the classes you want.

1. Load the configuration file

The file type

Here the configuration file takes the type application.properties

# the Java source code path handwritten. Framework. The app. Base_package = lzCopy the code

The utility class loads the properties file

A utility class is used to read configuration file information

package lz.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;


* * Then use the PropsUtil utility class to read the properties file * */
public final class PropsUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(PropsUtil.class);

    /** * Load the properties file */
    public static Properties loadProps(String fileName) {
        Properties props = null;
        InputStream is = null;
        try {
            is = ClassUtil.getClassLoader().getResourceAsStream(fileName);
            if (is == null) {
                throw new FileNotFoundException(fileName + " file is not found");
            }
            props = new Properties();
            props.load(is);
        } catch (IOException e) {
            LOGGER.error("load properties file failure", e);
        } finally {
            if(is ! =null) {
                try {
                    is.close();
                } catch (IOException e) {
                    LOGGER.error("close input stream failure", e); }}}return props;
    }

    /** * Gets the attribute value of type String (default is empty String) */
    public static String getString(Properties props, String key) {
        return getString(props, key, "");
    }

    /** * Gets a String attribute value (optionally a default value) */
    public static String getString(Properties props, String key, String defaultValue) {
        String value = defaultValue;
        if (props.containsKey(key)) {
            value = props.getProperty(key);
        }
        return value;
    }

    /** * gets an attribute value of type int (default is 0) */
    public static int getInt(Properties props, String key) {
        return getInt(props, key, 0);
    }

    /** * Gets the value of an attribute of type int (default can be specified) */
    public static int getInt(Properties props, String key, int defaultValue) {
        int value = defaultValue;
        if (props.containsKey(key)) {
            value = Integer.parseInt(props.getProperty(key));
        }
        return value;
    }

    /** * Get Boolean type attributes (default is false) */
    public static boolean getBoolean(Properties props, String key) {
        return getBoolean(props, key, false);
    }

    /** * Get Boolean type attributes (default values can be specified) */
    public static boolean getBoolean(Properties props, String key, boolean defaultValue) {
        boolean value = defaultValue;
        if (props.containsKey(key)) {
            value = Boolean.parseBoolean(props.getProperty(key));
        }
        returnvalue; }}Copy the code

Do a simple wrapper around the utility class

package lz.helper;

import lz.config.ConfigConstant;
import lz.utils.PropsUtil;

import java.util.Properties;


/* * The PropsUtil utility class is used to implement the ConfigHelper helper class. The framework uses the ConfigHelper class to load custom configuration files. If the user does not customize the configuration, the default configuration will be used
public final class ConfigHelper {

    /** * Load the properties of the configuration file */
    private static final Properties CONFIG_PROPS = PropsUtil.loadProps(ConfigConstant.CONFIG_FILE);

    /** * Get the JDBC driver */
    public static String getJdbcDriver(a) {
        return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.JDBC_DRIVER);
    }

    /** * get the JDBC URL */
    public static String getJdbcUrl(a) {
        return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.JDBC_URL);
    }

    /** * Get the JDBC user name */
    public static String getJdbcUsername(a) {
        return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.JDBC_USERNAME);
    }

    /** * Obtain the JDBC password */
    public static String getJdbcPassword(a) {
        return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.JDBC_PASSWORD);
    }

    /** * Obtain the basic application package name */
    public static String getAppBasePackage(a) {
        return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.APP_BASE_PACKAGE);
    }

    /** * Get the application JSP path */
    public static String getAppJspPath(a) {
        return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.APP_JSP_PATH, "/WEB-INF/view/");
    }

    /** * Obtain the application static resource path */
    public static String getAppAssetPath(a) {
        return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.APP_ASSET_PATH, "/asset/");
    }

    /** * Gets the attribute value of type String */ based on the attribute name
    public static String getString(String key) {
        return PropsUtil.getString(CONFIG_PROPS, key);
    }

    /** * Gets an attribute of type int */ based on the attribute name
    public static int getInt(String key) {
        return PropsUtil.getInt(CONFIG_PROPS, key);
    }

    /** * Gets a Boolean attribute value */ based on the attribute name
    public static boolean getBoolean(String key) {
        returnPropsUtil.getBoolean(CONFIG_PROPS, key); }}Copy the code

This is where you get all of your configuration information. If not, there are default values, so we can get the address of the packet you want to scan.

2. Class loading

Before instantiation, we need to load the Class and get all of the following classes with the specified package address. Also, we need to write an Util Class first

Generate class object

package lz.utils;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileFilter;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;


/* * The ClassUtil utility Class can get the Class Class by loading the fully qualified Class name and all Class classes with the specified package name. * */
public final class ClassUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(ClassUtil.class);

    /** * get the class loader */
    public static ClassLoader getClassLoader(a) {
        return Thread.currentThread().getContextClassLoader();
    }

    /** * Load class **@paramClassName the name of the class@paramIsInitialized Whether to initialize *@return* /
    public staticClass<? > loadClass(String className,booleanisInitialized) { Class<? > cls;try {
            cls = Class.forName(className, isInitialized, getClassLoader());
        } catch (ClassNotFoundException e) {
            LOGGER.error("load class failure", e);
            throw new RuntimeException(e);
        }
        return cls;
    }

    /** * Load the class (the class is initialized by default) */
    public staticClass<? > loadClass(String className) {return loadClass(className, true);
    }

    /** * get all classes */ under the specified package name
    public staticSet<Class<? >> getClassSet(String packageName) { Set<Class<? >> classSet =newHashSet<Class<? > > ();try {
            // Need to get the package of the famous car
            Enumeration<URL> urls = getClassLoader().getResources(packageName.replace("."."/"));
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                if(url ! =null) {
                    String protocol = url.getProtocol();
                    // If it is a folder
                    if (protocol.equals("file")) {
                        String packagePath = url.getPath().replaceAll("% 20"."");
                        addClass(classSet, packagePath, packageName);
                    } else if (protocol.equals("jar")) {
                        JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
                        if(jarURLConnection ! =null) {
                            JarFile jarFile = jarURLConnection.getJarFile();
                            if(jarFile ! =null) {
                                Enumeration<JarEntry> jarEntries = jarFile.entries();
                                while (jarEntries.hasMoreElements()) {
                                    JarEntry jarEntry = jarEntries.nextElement();
                                    String jarEntryName = jarEntry.getName();
                                    if (jarEntryName.endsWith(".class")) {
                                        String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/".".");
                                        doAddClass(classSet, className);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            LOGGER.error("get class set failure", e);
            throw new RuntimeException(e);
        }
        return classSet;
    }

    private static void addClass(Set
       
        > classSet, String packagePath, String packageName)
       > {
        // Save the files ending in class under the folder
        File[] files = new File(packagePath).listFiles(new FileFilter() {
            public boolean accept(File file) {
                return (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory(); }});// If you get a folder, go to the file under the folder
        for (File file : files) {
            String fileName = file.getName();
            if (file.isFile()) {
                String className = fileName.substring(0, fileName.lastIndexOf("."));
                if (StringUtils.isNotEmpty(packageName)) {
                    className = packageName + "." + className;
                }
                doAddClass(classSet, className);
            } else {
                String subPackagePath = fileName;
                if (StringUtils.isNotEmpty(packagePath)) {
                    subPackagePath = packagePath + "/" + subPackagePath;
                }
                String subPackageName = fileName;
                if (StringUtils.isNotEmpty(packageName)) {
                    subPackageName = packageName + "."+ subPackageName; } addClass(classSet, subPackagePath, subPackageName); }}}private static void doAddClass(Set
       
        > classSet, String className)
       > { Class<? > cls = loadClass(className,false);
        classSet.add(cls);
    }


    public static void main(String[] args) { Set<Class<? >> utils = getClassSet("lz/utils"); System.out.println(utils.size()); }}Copy the code

At this point, the class files under the specified package path have been loaded as class objects,

Similarly, write a corresponding helper to call the utility class

package lz.helper;

import lz.annotation.Controller;
import lz.annotation.Service;
import lz.utils.ClassUtil;

import java.lang.annotation.Annotation;
import java.util.HashSet;
import java.util.Set;

public final class ClassHelper {

    /** * Define a class set that holds all classes under the base package name */
    private static finalSet<Class<? >> CLASS_SET;static {
        // Get the base package name
        String basePackage = ConfigHelper.getAppBasePackage();
        System.out.println(basePackage);
        // Get all the classes under the base package name
        CLASS_SET = ClassUtil.getClassSet(basePackage);
        System.out.println("All classes under base package name" + CLASS_SET.size());
    }

    /** * get all the classes under the base package name */
    public staticSet<Class<? >> getClassSet() {return CLASS_SET;
    }

    /** * Get all the Service classes under the base package name */
    public staticSet<Class<? >> getServiceClassSet() { Set<Class<? >> classSet =newHashSet<Class<? > > ();for(Class<? > cls : CLASS_SET) {if(cls.isAnnotationPresent(Service.class)) { classSet.add(cls); }}return classSet;
    }

    /** * Get all the Controller classes under the base package name */
    public staticSet<Class<? >> getControllerClassSet() { Set<Class<? >> classSet =newHashSet<Class<? > > ();for(Class<? > cls : CLASS_SET) {if(cls.isAnnotationPresent(Controller.class)) { classSet.add(cls); }}return classSet;
    }

    /** * Get all Bean classes under the base package name (including: Controller, Service) */
    public staticSet<Class<? >> getBeanClassSet() { Set<Class<? >> beanClassSet =newHashSet<Class<? > > (); beanClassSet.addAll(getServiceClassSet()); beanClassSet.addAll(getControllerClassSet());return beanClassSet;
    }

    /** * Get all subclasses of a parent class of the base package name or all implementation classes of an interface */
    public staticSet<Class<? >> getClassSetBySuper(Class<? > superClass) { Set<Class<? >> classSet =newHashSet<Class<? > > ();for(Class<? > cls : CLASS_SET) {//isAssignableFrom() indicates whether superClass and CLS are the same or whether superClass is a parent of CLS
            if (superClass.isAssignableFrom(cls) && !superClass.equals(cls)) {
                classSet.add(cls);
            }
        }
        return classSet;
    }

    /** * get all classes with an annotation in the base package name */
    public staticSet<Class<? >> getClassSetByAnnotation(Class<? extends Annotation> annotationClass) { Set<Class<? >> classSet =newHashSet<Class<? > > ();for(Class<? > cls : CLASS_SET) {if(cls.isAnnotationPresent(annotationClass)) { classSet.add(cls); }}returnclassSet; }}Copy the code

3. Instantiate service and Controller objects

The current service object and controller object classes are already loaded into the classSet. You just need to separate them out and load them by calling the classloader, where the separation agent is in the ClassHelper. Let’s focus on the instantiation code

The instantiation code here calls the no-argument constructor, so the no-argument constructor must be preserved

package lz.helper;

import lz.utils.ReflectionUtil;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public final class BeanHelper {

    /** * BEAN_MAP is a Spring container with instances of all beans applied */
    private static finalMap<Class<? >, Object> BEAN_MAP =newHashMap<Class<? >, Object>();static {
        // Get all the beans in the application (annotated by service and Controller)Set<Class<? >> beanClassSet = ClassHelper.getBeanClassSet(); System.out.println("BeanClassSet size" + "* * * * * * * * * *" + beanClassSet.size());
        // Instantiate the bean and place it in the bean container
        for (Class<?> beanClass : beanClassSet) {
            Object obj = ReflectionUtil.newInstance(beanClass);
            System.out.println(obj);
            BEAN_MAP.put(beanClass, obj);
        }
    }

    /** * get the Bean container */
    public staticMap<Class<? >, Object> getBeanMap() {return BEAN_MAP;
    }

    /** * get the Bean instance */

    public static <T> T getBean(Class<T> cls) {
        if(! BEAN_MAP.containsKey(cls)) {throw new RuntimeException("can not get lz.bean by class: " + cls);
        }
        return (T) BEAN_MAP.get(cls);
    }

    /** * Sets the Bean instance */
    public static void setBean(Class
        cls, Object obj) { BEAN_MAP.put(cls, obj); }}Copy the code

The instantiation here uses reflection

package lz.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public final class ReflectionUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(ReflectionUtil.class);

    /** * Create instance */
    public static Object newInstance(Class
        cls) {
        Object instance;
        try {
            instance = cls.newInstance();
        } catch (Exception e) {
            LOGGER.error("new instance failure", e);
            throw new RuntimeException(e);
        }
        return instance;
    }

    /** * Create instance (based on class name) */
    public static Object newInstance(String className) { Class<? > cls = ClassUtil.loadClass(className);return newInstance(cls);
    }

    /** * calls the method */
    public static Object invokeMethod(Object obj, Method method, Object... args) {
        Object result;
        try {
            method.setAccessible(true);
            result = method.invoke(obj, args);
        } catch (Exception e) {
            LOGGER.error("invoke method failure", e);
            throw new RuntimeException(e);
        }
        return result;
    }

    /** * sets the value of the member variable */
    public static void setField(Object obj, Field field, Object value) {
        try {
            field.setAccessible(true); // Remove private permissions
            field.set(obj, value);
        } catch (Exception e) {
            LOGGER.error("set field failure", e);
            throw newRuntimeException(e); }}}Copy the code

You’ve done it up here you’ve done the instantiation, but you haven’t injected the properties yet

4. Property injection

package lz.helper;

import lz.annotation.Autowired;
import lz.utils.ReflectionUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.ArrayUtils;

import java.lang.reflect.Field;
import java.util.Map;
import java.util.Set;

public final class IocHelper {

    /** * traverses all bean properties in the bean container for all bands@AutowiredAnnotated property injection instance */
    static {
        // Iterate over all the beans in the bean containerMap<Class<? >, Object> beanMap = BeanHelper.getBeanMap();if (MapUtils.isNotEmpty(beanMap)) {
            for(Map.Entry<Class<? >, Object> beanEntry : beanMap.entrySet()) {/ / the bean's class classClass<? > beanClass = beanEntry.getKey();/ / the instance of the bean
                Object beanInstance = beanEntry.getValue();
                // Violent reflection gets attributes
                Field[] beanFields = beanClass.getDeclaredFields();
                // Iterate over the bean's properties
                if (ArrayUtils.isNotEmpty(beanFields)) {
                    for (Field beanField : beanFields) {
                        // Determine if the attribute has an Autowired annotation
                        if (beanField.isAnnotationPresent(Autowired.class)) {
                            // Attribute typeClass<? > beanFieldClass = beanField.getType();// If beanFieldClass is the interface, get the implementation class corresponding to the interface
                            beanFieldClass = findImplementClass(beanFieldClass);
                            // Get the corresponding instance of Class
                            Object beanFieldInstance = beanMap.get(beanFieldClass);
                            if(beanFieldInstance ! =null) {
                                ReflectionUtil.setField(beanInstance, beanField, beanFieldInstance);
                            }
                        }
                    }
                }
            }
        }
    }

    /** * get the implementation class */ corresponding to the interface
    public staticClass<? > findImplementClass(Class<? > interfaceClass) { Class<? > implementClass = interfaceClass;// All implementation classes corresponding to the interfaceSet<Class<? >> classSetBySuper = ClassHelper.getClassSetBySuper(interfaceClass);if (CollectionUtils.isNotEmpty(classSetBySuper)) {
            // Get the first implementation class
            implementClass = classSetBySuper.iterator().next();
        }
        returnimplementClass; }}Copy the code

At this point, a full bean container is implemented, which naturally solves the problem of cyclic dependencies

Let’s test it out

public final class HelperLoader {

    public static void init(a) { Class<? >[] classList = { ClassHelper.class, BeanHelper.class, IocHelper.class, ControllerHelper.class };for (Class<?> cls : classList) {
            ClassUtil.loadClass(cls.getName());
            System.out.println(cls.getName());
        }
    }

    public static void main(String[] args) {
        HelperLoader.init();
        Map<Class<?>, Object> beanMap = BeanHelper.getBeanMap();
        System.out.println(beanMap.size());
        
    }
}
Copy the code

3.2 the MVC function

Here you need to start with Tomcat, you can refer to the Architecture of springMVC,

There needs to be a Controller DispatchServlet

1, the controller

First, create a controller

package lz.controller;

import com.alibaba.fastjson.JSON;
import lz.annotation.Autowired;
import lz.annotation.Controller;
import lz.annotation.RequestMapping;
import lz.annotation.RequestMethod;
import lz.bean.Data;
import lz.bean.View;
import lz.domain.User;
import lz.service.IUserService;

import java.util.List;

@Controller
public class UserController {
    @Autowired
    private IUserService userService;

    /** * User list **@return* /
    @RequestMapping(value = "/userList", method = RequestMethod.GET)
    public Data getUserList(a) {
        List<User> userList = userService.getAllUser();
        return new Data("123213123"); }}Copy the code

2. Request-processor build

According to

package lz.mvc;

import lz.annotation.RequestMapping;
import lz.bean.Handler;
import lz.bean.Request;
import lz.helper.ClassHelper;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/** * The controller helper class acts as a mapping handler in SpringMVC, setting the corresponding handler for the request URI */
public final class ControllerHelper {

    /** * REQUEST_MAP is a "request-processor" mapping */
    private static final Map<Request, Handler> REQUEST_MAP = new HashMap<Request, Handler>();

    static {
        // Iterate through all Controller classesSet<Class<? >> controllerClassSet = ClassHelper.getControllerClassSet();if (CollectionUtils.isNotEmpty(controllerClassSet)) {
            for(Class<? > controllerClass : controllerClassSet) {// Violent reflection gets all methods
                Method[] methods = controllerClass.getDeclaredMethods();
                // the traversal method
                if (ArrayUtils.isNotEmpty(methods)) {
                    for (Method method : methods) {
                        // Check whether RequestMapping annotations are included
                        if (method.isAnnotationPresent(RequestMapping.class)) {
                            RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                            // Request path
                            String requestPath = requestMapping.value();
                            // Request method
                            String requestMethod = requestMapping.method().name();

                            // Encapsulate the request and handler
                            Request request = new Request(requestMethod, requestPath);
                            Handler handler = new Handler(controllerClass, method);
                            REQUEST_MAP.put(request, handler);
                        }
                    }
                }
            }
        }
    }

    /** * get Handler */
    public static Handler getHandler(String requestMethod, String requestPath) {
        Request request = new Request(requestMethod, requestPath);
        returnREQUEST_MAP.get(request); }}Copy the code

3. Front-end controller

package lz.mvc;

import lz.HelperLoader;
import lz.bean.Data;
import lz.bean.Handler;
import lz.bean.Param;
import lz.bean.View;
import com.alibaba.fastjson.JSON;
import lz.helper.BeanHelper;
import lz.helper.ConfigHelper;
import org.apache.commons.lang3.StringUtils;
import lz.utils.*;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.Map;

@WebServlet(urlPatterns = "/*", loadOnStartup = 0)
public class DispatcherServlet extends HttpServlet {

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        // Initializes the related helper classes
        HelperLoader.init();

        // Get the ServletContext object used to register the Servlet
        ServletContext servletContext = servletConfig.getServletContext();

        // Register servlets that handle JSPS and static resources
        registerServlet(servletContext);
    }

    / * * * DefaultServlet and JspServlet are created by the Web container * org in apache. Catalina. Servlets. DefaultServlet * org.apache.jasper.servlet.JspServlet */
    private void registerServlet(ServletContext servletContext) {
        // Dynamically register servlets that handle JSPS
        ServletRegistration jspServlet = servletContext.getServletRegistration("jsp");
        jspServlet.addMapping(ConfigHelper.getAppJspPath() + "*");

        // Dynamically register default servlets that handle static resources
        ServletRegistration defaultServlet = servletContext.getServletRegistration("default");
        defaultServlet.addMapping("/favicon.ico"); // Site avatar
        defaultServlet.addMapping(ConfigHelper.getAppAssetPath() + "*");
    }

    @Override
    public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String requestMethod = request.getMethod().toUpperCase();
        String requestPath = request.getPathInfo();

        // There are two cases according to Tomcat configuration path, one is "/userList", the other is "/context address /userList".
        String[] splits = requestPath.split("/");
        if (splits.length > 2) {
            requestPath = "/" + splits[2];
        }

        // Get the handler on request (this is similar to the mapping handler in SpringMVC)
        Handler handler = ControllerHelper.getHandler(requestMethod, requestPath);
        if(handler ! =null) { Class<? > controllerClass = handler.getControllerClass(); Object controllerBean = BeanHelper.getBean(controllerClass);// Initialize parameters
            Param param = RequestHelper.createParam(request);

            // Invoke the method corresponding to the request (this is similar to the processor adapter in SpringMVC)
            Object result;
            Method actionMethod = handler.getControllerMethod();
            if (param == null || param.isEmpty()) {
                result = ReflectionUtil.invokeMethod(controllerBean, actionMethod);
            } else {
                result = ReflectionUtil.invokeMethod(controllerBean, actionMethod, param);
            }

            // Jump to the page or return JSON data (here similar to the view parser in SpringMVC)
            if (result instanceof View) {
                handleViewResult((View) result, request, response);
            } else if (result instanceofData) { handleDataResult((Data) result, response); }}}/** * jump to the page */
    private void handleViewResult(View view, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        String path = view.getPath();
        if (StringUtils.isNotEmpty(path)) {
            if (path.startsWith("/")) { / / redirection
                response.sendRedirect(request.getContextPath() + path);
            } else { // Request forwarding
                Map<String, Object> model = view.getModel();
                for(Map.Entry<String, Object> entry : model.entrySet()) { request.setAttribute(entry.getKey(), entry.getValue()); } request.getRequestDispatcher(ConfigHelper.getAppJspPath() + path).forward(request, response); }}}/** * returns JSON data */
    private void handleDataResult(Data data, HttpServletResponse response) throws IOException {
        Object model = data.getModel();
        if(model ! =null) {
            response.setContentType("application/json");
            response.setCharacterEncoding("UTF-8"); PrintWriter writer = response.getWriter(); String json = JSON.toJSON(model).toString(); System.out.println(json); writer.write(json); writer.flush(); writer.close(); }}}Copy the code

Request parameters are encapsulated

package lz.mvc;

import lz.bean.Param;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

public final class RequestHelper {

    /** * get request parameters */
    public static Param createParam(HttpServletRequest request) throws IOException {
        Map<String, Object> paramMap = new HashMap();
        Enumeration<String> paramNames = request.getParameterNames();
        // No arguments
        if(! paramNames.hasMoreElements()) {return null;
        }

        // Both get and POST parameters can be obtained
        while (paramNames.hasMoreElements()) {
            String fieldName = paramNames.nextElement();
            String fieldValue = request.getParameter(fieldName);
            paramMap.put(fieldName, fieldValue);
        }

        return newParam(paramMap); }}Copy the code
<%@ page pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<c:set var="BASE" value="${pageContext.request.contextPath}"/>

<html>
<head>
    <title>The user information</title>
</head>
<body>

<h1>The user information</h1>

<table>
    <tr>
        <th>The user id</th>
        <th>The name of the</th>
        <th>age</th>
    </tr>
    <c:forEach var="userinfo" items="${userList}">
        <tr>
            <td>${userinfo.id}</td>
            <td>${userinfo.name}</td>
            <td>${userinfo.age}</td>
            <td>
                <a href="${BASE}/userInfo? id=${userinfo.id}">details</a>
                <a href="${BASE}/userEdit? id=${userinfo.id}">The editor</a>
            </td>
        </tr>
    </c:forEach>
</table>

</body>
</html>
Copy the code

Start the test

3.3 AOP functionality

The previous ioc and MVC functions have been implemented, the AOP function began to implement the following, using the chain call scheme, will be a number of proxies in order to call

The code here will be a little more complex, so I’ll give you a general idea of what to expect if you’re not familiar with aop invocation estimates

  • First, we create an Aspect annotation to indicate that this is a facet, (simple)
  • Create proxy and AspectProxy. The former is used to implement chained calls, and the latter is used to define the timing of the rollback before and after. It is an abstract class in which the related methods are waiting to be populated (simple)
  • Creating a traditional aspect form, this article EfficientAspect, inherited from AspectProxy (simple)

Now that you’ve defined the cuts and pointcuts, all you need to do is cut the cuts into your prosteed object using Aophelper

  • First, when scanning the package, you need to scan all the AspectProxy implementation classes, which are the facets
  • Need to save the two hashes of the section – proxied, proxied class – section class
  • The proxy object is then created using CGLIB
  • Then, when calling the target method, it will cut into the Intercept of Cglib. The chain call is adopted here. Through the section class of the proxied object -, a chain can be successfully constructed and then recursive call is made according to the length of the linked list

– CGLIB does not fetch member variables from the proxied object. All methods called by CGLIB need to fetch the proxied object and execute

Don’t understand the step by step debugging, this first look around

1. Create section annotations

package lz.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Aspect {
    /**
     * 包名
     */
    String pkg(a) default "";

    /** * Class name */
    String cls(a) default "";
}

Copy the code

2. Proxy class creation

package lz.proxy;

public interface Proxy {

    A chain agent is a chain of agents that can be executed one by one, depending on the order in which they are added to the chain
    Object doProxy(ProxyChain proxyChain) throws Throwable;
}

Copy the code
package lz.proxy;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;

public abstract class AspectProxy implements Proxy {

    private static final Logger logger = LoggerFactory.getLogger(AspectProxy.class);

    @Override
    public final Object doProxy(ProxyChain proxyChain) throws Throwable {
        Object result = null; Class<? > cls = proxyChain.getTargetClass(); Method method = proxyChain.getTargetMethod(); Object[] params = proxyChain.getMethodParams(); begin();try {
            if (intercept(method, params)) {
                before(method, params);
                result = proxyChain.doProxyChain();
                after(method, params);
            } else{ result = proxyChain.doProxyChain(); }}catch (Exception e) {
            logger.error("proxy failure", e);
            error(method, params, e);
            throw e;
        } finally {
            end();
        }
        return result;
    }

    /** * start to enhance */
    public void begin(a) {}/** * pointcut judgment */
    public boolean intercept(Method method, Object[] params) throws Throwable {
        return true;
    }

    /** ** ** */
    public void before(Method method, Object[] params) throws Throwable {}/** * post-enhanced */
    public void after(Method method, Object[] params) throws Throwable {}/** * Exception enhancement */
    public void error(Method method, Object[] params, Throwable e) {}/** * finally enhanced */
    public void end(a) {}}Copy the code
package lz.aspect;

import lz.annotation.Aspect;
import lz.proxy.AspectProxy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;

@Aspect(pkg = "lz.controller", cls = "UserController")
public class EfficientAspect extends AspectProxy {

    private static final Logger LOGGER = LoggerFactory.getLogger(EfficientAspect.class);

    private long begin;

    /** * pointcut judgment */
    @Override
    public boolean intercept(Method method, Object[] params) throws Throwable {
        return method.getName().equals("getUserList");
    }

    @Override
    public void before(Method method, Object[] params) throws Throwable {
        System.out.println("---------- begin ----------");
        begin = System.currentTimeMillis();
    }

    @Override
    public void after(Method method, Object[] params) throws Throwable {
        System.out.println(String.format("time: %dms", System.currentTimeMillis() - begin));
        System.out.println("----------- end -----------"); }}Copy the code
package lz.proxy;


import lz.helper.BeanHelper;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class ProxyChain {

    private finalClass<? > targetClass;/ / the target class
    private final Object targetObject; // Target object
    private final Method targetMethod; // Target method
    private final MethodProxy methodProxy; // Method proxy
    private final Object[] methodParams; // Method parameters

    private List<Proxy> proxyList = new ArrayList<>(); // List of agents
    private int proxyIndex = 0; // Proxy index

    public ProxyChain(Class
        targetClass, Object targetObject, Method targetMethod, MethodProxy methodProxy, Object[] methodParams, List
       
         proxyList)
        {
        this.targetClass = targetClass;
        this.targetObject = targetObject;
        this.targetMethod = targetMethod;
        this.methodProxy = methodProxy;
        this.methodParams = methodParams;
        this.proxyList = proxyList;

    }

    public Object[] getMethodParams() {
        return methodParams;
    }

    publicClass<? > getTargetClass() {return targetClass;
    }

    public Method getTargetMethod(a) {
        return targetMethod;
    }

    /** * recursively executes */
    public Object doProxyChain(a) throws Throwable {
        Object methodResult;
        if (proxyIndex < proxyList.size()) {
            // Perform the enhancement method
            methodResult = proxyList.get(proxyIndex++).doProxy(this);
        } else {
            // The target method is executed last and only once
            Object bean_proxy = BeanHelper.getBean_proxy(targetClass);
            methodResult = targetMethod.invoke(bean_proxy, methodParams);
        }
        returnmethodResult; }}Copy the code
package lz.proxy;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import java.util.List;

public class ProxyFactory {

    /** * input a target class and a set of Proxy interface implementations, output a Proxy object */
    @SuppressWarnings("unchecked")
    public static <T> T createProxy(finalClass<? > targetClass,final List<Proxy> proxyList) {

        return (T) Enhancer.create(targetClass, new MethodInterceptor() {
            /** * creates a ProxyChain object and calls the doProxyChain() method of that object each time the target method is called. */
            @Override
            // When the target method is called, it automatically enters the method. This method creates a chain and calls through the chain
            public Object intercept(Object targetObject, Method targetMethod, Object[] methodParams, MethodProxy methodProxy) throws Throwable {
                return newProxyChain(targetClass, targetObject, targetMethod, methodProxy, methodParams, proxyList).doProxyChain(); }}); }}Copy the code

AOPhelper is used to load AOP functionality

package lz.helper;

import lz.annotation.Aspect;
import lz.annotation.Service;
import lz.proxy.AspectProxy;
import lz.proxy.Proxy;
import lz.proxy.ProxyFactory;
import lz.proxy.TransactionProxy;
import lz.utils.ClassUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

public final class AopHelper {

    private static final Logger LOGGER = LoggerFactory.getLogger(AopHelper.class);

    static {
        try {
            // The mapping of the section class to the target class collectionMap<Class<? >, Set<Class<? >>> aspectMap = createAspectMap();// Target class - mapping to the list of slice objectsMap<Class<? >, List<Proxy>> targetMap = createTargetMap(aspectMap);// Create a proxy object by weaving the slice object into the target class
            for(Map.Entry<Class<? >, List<Proxy>> targetEntry : targetMap.entrySet()) { Class<? > targetClass = targetEntry.getKey(); List<Proxy> proxyList = targetEntry.getValue(); Object proxy = ProxyFactory.createProxy(targetClass, proxyList);// Overwrite the instance of the target class in the Bean container. The next thing you get from the Bean container is a proxy object
                BeanHelper.setBean_proxy(targetClass, BeanHelper.getBean(targetClass));
                // Used to get the proxied object, otherwise member variables cannot be obtained in the proxied objectBeanHelper.setBean(targetClass, proxy); }}catch (Exception e) {
            LOGGER.error("aop failure", e); }}/** * gets the mapping of the section class to the target class collection */
    private staticMap<Class<? >, Set<Class<? >>> createAspectMap()throwsException { Map<Class<? >, Set<Class<? >>> aspectMap =newHashMap<Class<? >, Set<Class<? > > > (); addAspectProxy(aspectMap); addTransactionProxy(aspectMap);return aspectMap;
    }

    /** * gets the mapping of the normal section class to the target class set */
    private static void addAspectProxy(Map
       
        , Set
        
         >> aspectMap)
        >
       > throws Exception {
        // All aspects that implement the AspectProxy abstract classSet<Class<? >> aspectClassSet = ClassHelper.getClassSetBySuper(AspectProxy.class);for(Class<? > aspectClass : aspectClassSet) {if (aspectClass.isAnnotationPresent(Aspect.class)) {
                Aspect aspect = aspectClass.getAnnotation(Aspect.class);
                // The set of target classes corresponding to the sliceSet<Class<? >> targetClassSet = createTargetClassSet(aspect); aspectMap.put(aspectClass, targetClassSet); }}}/** * gets the mapping of the transaction-aspect class to the target class collection */
    private static void addTransactionProxy(Map
       
        , Set
        
         >> aspectMap)
        >
       > { Set<Class<? >> serviceClassSet = ClassHelper.getClassSetByAnnotation(Service.class); aspectMap.put(TransactionProxy.class, serviceClassSet); }/ * * * according to@AspectDefine the package name and class name to get the corresponding target class collection */
    private staticSet<Class<? >> createTargetClassSet(Aspect aspect)throwsException { Set<Class<? >> targetClassSet =newHashSet<Class<? > > ();/ / package name
        String pkg = aspect.pkg();
        / / the name of the class
        String cls = aspect.cls();
        // If neither package name nor class name is empty, add the specified class
        if(! pkg.equals("") && !cls.equals("")) {
            targetClassSet.add(Class.forName(pkg + "." + cls));
        } else if(! pkg.equals("")) {
            // If the package name is not empty but the class name is empty, all classes under the package name are added
            targetClassSet.addAll(ClassUtil.getClassSet(pkg));
        }
        return targetClassSet;
    }

    /** * transforms the mapping between the section class and the target class collection into the mapping between the target class and the list of section objects, where there may be more than one */
    private staticMap<Class<? >, List<Proxy>> createTargetMap(Map<Class<? >, Set<Class<? >>> aspectMap)throwsException { Map<Class<? >, List<Proxy>> targetMap =newHashMap<Class<? >, List<Proxy>>();for(Map.Entry<Class<? >, Set<Class<? >>> proxyEntry : aspectMap.entrySet()) {/ / cut classClass<? > aspectClass = proxyEntry.getKey();// Target class collectionSet<Class<? >> targetClassSet = proxyEntry.getValue();// Create a mapping between the target class and the list of cut objects
            for(Class<? > targetClass : targetClassSet) {// Cut the object
                Proxy aspect = (Proxy) aspectClass.newInstance();
                if (targetMap.containsKey(targetClass)) {
                    targetMap.get(targetClass).add(aspect);
                } else {
                    // List of slice objects
                    List<Proxy> aspectList = newArrayList<Proxy>(); aspectList.add(aspect); targetMap.put(targetClass, aspectList); }}}returntargetMap; }}Copy the code

DoProxyChain -> Select an object in the linked list

DoProxy -> starts calling the proxy method in the selected object (section class) -> executes to result = proxychain-doproxychain (); A static proxy would have executed the current method, but it chose to find the call chain again

DoProxyChain -> Selects the next object in the linked list based on the index index

If there is only one facet, index = 0, then the proxied object’s method can be executed

If there are multiple cuts, index! = 0, I need to go back to doProxy

When all of the pre-notification has been executed, index = 0, the propped object’s method can be executed, and finally return (a recursive operation here!! There are several levels of recursion.)

When multiple facets are proxying an object at the same time, the order is first scanned, then pressed into the list, and then called. If you specify this, you can add a parameter pripority for sorting

3.4 transactions

Spring transactions are based on database transaction support. The simplest way to do transactions is to turn off auto-commit and do it yourself step by step. Add @Transaction as a proxy and AOP is at the bottom.

In this case, we choose to proxy all the service layers that are added to @Servie, and then determine whether the method is added to @Transaction. If it is added to @Transaction, we will use the transaction method to call it. If not, Call the normal call is ok

1. Database

package lz.helper;

import lz.helper.ConfigHelper;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/** * database operator class **@author litianxiang
 */
public final class DatabaseHelper {

    private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseHelper.class);

    private static final ThreadLocal<Connection> CONNECTION_HOLDER;

    private static final QueryRunner QUERY_RUNNER;

    private static final BasicDataSource DATA_SOURCE;

    static {
        CONNECTION_HOLDER = new ThreadLocal<Connection>();

        QUERY_RUNNER = new QueryRunner();

        DATA_SOURCE = new BasicDataSource();
        DATA_SOURCE.setDriverClassName(ConfigHelper.getJdbcDriver());
        DATA_SOURCE.setUrl(ConfigHelper.getJdbcUrl());
        DATA_SOURCE.setUsername(ConfigHelper.getJdbcUsername());
        DATA_SOURCE.setPassword(ConfigHelper.getJdbcPassword());
    }

    /** * get the data source */
    public static DataSource getDataSource(a) {
        return DATA_SOURCE;
    }

    /** * get database connection */
    public static Connection getConnection(a) {
        Connection conn = CONNECTION_HOLDER.get();
        if (conn == null) {
            try {
                conn = DATA_SOURCE.getConnection();
            } catch (SQLException e) {
                LOGGER.error("get connection failure", e);
                throw new RuntimeException(e);
            } finally{ CONNECTION_HOLDER.set(conn); }}return conn;
    }

    /** * start transaction */
    public static void beginTransaction(a) {
        Connection conn = getConnection();
        if(conn ! =null) {
            try {
                conn.setAutoCommit(false);
            } catch (SQLException e) {
                LOGGER.error("begin transaction failure", e);
                throw new RuntimeException(e);
            } finally{ CONNECTION_HOLDER.set(conn); }}}/** * Commit transaction */
    public static void commitTransaction(a) {
        Connection conn = getConnection();
        if(conn ! =null) {
            try {
                conn.commit();
                conn.close();
            } catch (SQLException e) {
                LOGGER.error("commit transaction failure", e);
                throw new RuntimeException(e);
            } finally{ CONNECTION_HOLDER.remove(); }}}/** * rollback transaction */
    public static void rollbackTransaction(a) {
        Connection conn = getConnection();
        if(conn ! =null) {
            try {
                conn.rollback();
                conn.close();
            } catch (SQLException e) {
                LOGGER.error("rollback transaction failure", e);
                throw new RuntimeException(e);
            } finally{ CONNECTION_HOLDER.remove(); }}}/** * Query entity */
    public static <T> T queryEntity(Class<T> entityClass, String sql, Object... params) {
        T entity;
        try {
            Connection conn = getConnection();
            entity = QUERY_RUNNER.query(conn, sql, new BeanHandler<T>(entityClass), params);
        } catch (SQLException e) {
            LOGGER.error("query entity failure", e);
            throw new RuntimeException(e);
        }
        return entity;
    }

    /** * Query entity list */
    public static <T> List<T> queryEntityList(Class<T> entityClass, String sql, Object... params) {
        List<T> entityList;
        try {
            Connection conn = getConnection();
            entityList = QUERY_RUNNER.query(conn, sql, new BeanListHandler<T>(entityClass), params);
        } catch (SQLException e) {
            LOGGER.error("query entity list failure", e);
            throw new RuntimeException(e);
        }
        return entityList;
    }

    /** * Execute update statements (including: update, INSERT, delete) */
    public static int update(String sql, Object... params) {
        int rows;
        try {
            Connection conn = getConnection();
            rows = QUERY_RUNNER.update(conn, sql, params);
        } catch (SQLException e) {
            LOGGER.error("execute update failure", e);
            throw new RuntimeException(e);
        }
        return rows;
    }

    /** * insert entity */
    public static <T> boolean insertEntity(Class<T> entityClass, Map<String, Object> fieldMap) {
        if (MapUtils.isEmpty(fieldMap)) {
            LOGGER.error("can not insert entity: fieldMap is empty");
            return false;
        }

        String sql = "INSERT INTO " + entityClass.getSimpleName();
        StringBuilder columns = new StringBuilder("(");
        StringBuilder values = new StringBuilder("(");
        for (String fieldName : fieldMap.keySet()) {
            columns.append(fieldName).append(",");
            values.append("?, ");
        }
        columns.replace(columns.lastIndexOf(","), columns.length(), ")");
        values.replace(values.lastIndexOf(","), values.length(), ")");
        sql += columns + " VALUES " + values;

        Object[] params = fieldMap.values().toArray();

        return update(sql, params) == 1;
    }

    /** * Update entity */
    public static <T> boolean updateEntity(Class<T> entityClass, long id, Map<String, Object> fieldMap) {
        if (MapUtils.isEmpty(fieldMap)) {
            LOGGER.error("can not update entity: fieldMap is empty");
            return false;
        }

        String sql = "UPDATE " + entityClass.getSimpleName() + " SET ";
        StringBuilder columns = new StringBuilder();
        for (String fieldName : fieldMap.keySet()) {
            columns.append(fieldName).append("=? ,");
        }
        sql += columns.substring(0, columns.lastIndexOf(",")) + " WHERE id = ?";

        List<Object> paramList = new ArrayList<Object>();
        paramList.addAll(fieldMap.values());
        paramList.add(id);
        Object[] params = paramList.toArray();

        return update(sql, params) == 1;
    }

    /** * Delete entity */
    public static <T> boolean deleteEntity(Class<T> entityClass, long id) {
        String sql = "DELETE FROM " + entityClass.getSimpleName() + " WHERE id = ?";
        return update(sql, id) == 1; }}Copy the code

2. Transaction agent

package lz.proxy;

import lz.annotation.Transactional;
import lz.helper.DatabaseHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;


public class TransactionProxy implements Proxy {

    private static final Logger LOGGER = LoggerFactory.getLogger(TransactionProxy.class);

    @Override
    public Object doProxy(ProxyChain proxyChain) throws Throwable {
        Object result;
        Method method = proxyChain.getTargetMethod();
        // methods annotated with the @transactional annotation do transactions
        if (method.isAnnotationPresent(Transactional.class)) {
            try {
                DatabaseHelper.beginTransaction();
                LOGGER.debug("begin transaction");
                result = proxyChain.doProxyChain();
                DatabaseHelper.commitTransaction();
                LOGGER.debug("commit transaction");
            } catch (Exception e) {
                DatabaseHelper.rollbackTransaction();
                LOGGER.debug("rollback transaction");
                throwe; }}else {
            result = proxyChain.doProxyChain();
        }
        returnresult; }}Copy the code

3, the service

package lz.service.Impl;

import lz.annotation.Autowired;
import lz.annotation.Service;
import lz.annotation.Transactional;
import lz.domain.User;
import lz.helper.DatabaseHelper;
import lz.service.IUserService;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;


@Service
public class UserService implements IUserService {
    /** * Get all users */
    @Override
    public List<User> getAllUser(a) {
        String sql = "SELECT * FROM user";
        return DatabaseHelper.queryEntityList(User.class, sql);
    }

    /** * Get user information based on id */
    @Override
    public User GetUserInfoById(Integer id) {
        String sql = "SELECT * FROM user WHERE id = ?";
        return DatabaseHelper.queryEntity(User.class, sql, id);
    }

    /** * Modify user information */
    @Transactional
    @Override
    public boolean updateUser(int id, Map<String, Object> fieldMap) {
        returnDatabaseHelper.updateEntity(User.class, id, fieldMap); }}Copy the code

4, the controller

package lz.controller;

import lz.annotation.Autowired;
import lz.annotation.Controller;
import lz.annotation.RequestMapping;
import lz.annotation.RequestMethod;
import lz.bean.Data;
import lz.bean.Param;
import lz.bean.View;
import lz.domain.User;
import lz.service.IUserService;


import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Controller
public class UserController {
    @Autowired
    private IUserService userService;

    /** * User list **@return* /
    @RequestMapping(value = "/userList", method = RequestMethod.GET)
    public View getUserList(a) {
        List<User> userList = userService.getAllUser();
        return new View("index.jsp").addModel("userList", userList);
    }

    /** * User details **@param param
     * @return* /
    @RequestMapping(value = "/userInfo", method = RequestMethod.GET)
    public Data getUserInfo(Param param) {
        String id = (String) param.getParamMap().get("id");
        User user = userService.GetUserInfoById(Integer.parseInt(id));

        return new Data(user);
    }

    @RequestMapping(value = "/userEdit", method = RequestMethod.GET)
    public Data editUser(Param param) {
        String id = (String) param.getParamMap().get("id");
        Map<String, Object> fieldMap = new HashMap<>();
        fieldMap.put("age".911);
        userService.updateUser(Integer.parseInt(id), fieldMap);
        return new Data("Success."); }}Copy the code

Test using Tomcat

4, summarize

In this paper, the implementation of spring IOC/AOP/MVC/ transaction function, the main code is a reference to the above, and the appropriate modification of it (there are some small errors in the code), if you think of tomcat build trouble, write a few test classes to test, the main difficulty in the CHAIN of AOP call over there, The others are easy to understand

Recall how AOP and IOC used to learn Spring source code to click on a method, and click on a powder keg

5, reference

Blog.csdn.net/litianxiang…