1、相关依赖
代码文件:github.com/lzlz123/spr… (我这里面的)
github.com/tyshawnlee/… (参考的)
(参考代码,这里面AOP代码有错误,代理对象的并没有注入依赖,所以会报空指针异常,解决方案主要有两个,1、将被代理对象的进行依赖注入,2、获得到被代理对象,通过被代理对象反射执行原来的方法,发现Cglib好像没法通过代理对象获得被代理对象,所以新增加了一个HashMap,在代理对象依赖注入的时候,进行保存)
<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标准标签库 -->
<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>
<!--数据库连接池-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.0.1</version>
</dependency>
<!--JDBC工具类库-->
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.6</version>
</dependency>
<!-- 日志框架 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.7</version>
</dependency>
<!-- 动态代理依赖 -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
<!-- 通用工具包 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.3.2</version>
</dependency>
<!--集合工具包-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
<!--JSON依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.49</version>
</dependency>
</dependencies>
复制代码
2、本文的工作
-
实现一个 bean 容器
-
实现容器的IOC 功能
Bean容器:
Spring是一个容器, 管理着应用中所有bean的装配和生命周期, Spring容器其实是一个Map映射, 里面存储了应用中所有bean的实例, key为该bean实例的Class对象. Spring有两种容器, 分别是 BeanFactory 和 ApplicationContext, 二者的区别在于, BeanFactory采用延迟加载策略, 在第一次调用getBean()时才真正装配该对象. 而 ApplicationContext会在应用启动时就把所有对象一次性全部装配好.
IOC: IOC的实现思路如下:
- 首先有一个配置文件定义了应用的基础包, 也就是Java源码路径.
- 读取基础包名, 然后通过类加载器获取到应用中所有的Class对象, 存储到一个集合中.
- 获取应用中所有Bean (Controller和Service) 的Class对象, 通过反射创建实例, 然后存储到 Bean容器中.
- 遍历Bean容器中的所有Bean, 为所有带 @Autowired 注解的属性注入实例.
- IOC操作要在应用启动时就完成, 所以必须写在静态代码块中.
3、具体功能实现方案
3.1一些前置的代码
用于实现依赖注入
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}
复制代码
用于实现 controller
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}
复制代码
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
/**
* 请求路径
*
* @return
*/
String value() default "";
/**
* 请求方法
*
* @return
*/
RequestMethod method() default RequestMethod.GET;
}
复制代码
public enum RequestMethod {
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
}
复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {
}
复制代码
3.2 IOC功能
通过给定一个配置(这里暂时采用properties文件配置),可以扫描包下面自己所需要的类(这里采用注解,当注解为类service或者controller时候),将所需要的类的clss文件进行保存。
1、加载配置文件
文件类型
这里配置文件采用 application.properties
这种类型
#java源码路径
handwritten.framework.app.base_package=lz
复制代码
工具类加载 properties 文件
采用一个工具类进行读取配置文件的信息
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;
/*
* 然后使用 PropsUtil 工具类来读取属性文件
* */
public final class PropsUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(PropsUtil.class);
/**
* 加载属性文件
*/
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;
}
/**
* 获取 String 类型的属性值(默认值为空字符串)
*/
public static String getString(Properties props, String key) {
return getString(props, key, "");
}
/**
* 获取 String 类型的属性值(可指定默认值)
*/
public static String getString(Properties props, String key, String defaultValue) {
String value = defaultValue;
if (props.containsKey(key)) {
value = props.getProperty(key);
}
return value;
}
/**
* 获取 int 类型的属性值(默认值为 0)
*/
public static int getInt(Properties props, String key) {
return getInt(props, key, 0);
}
/**
* 获取 int 类型的属性值(可指定默认值)
*/
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;
}
/**
* 获取 boolean 类型属性(默认值为 false)
*/
public static boolean getBoolean(Properties props, String key) {
return getBoolean(props, key, false);
}
/**
* 获取 boolean 类型属性(可指定默认值)
*/
public static boolean getBoolean(Properties props, String key, boolean defaultValue) {
boolean value = defaultValue;
if (props.containsKey(key)) {
value = Boolean.parseBoolean(props.getProperty(key));
}
return value;
}
}
复制代码
对工具类进行一次简单的封装
package lz.helper;
import lz.config.ConfigConstant;
import lz.utils.PropsUtil;
import java.util.Properties;
/*
* 借助 PropsUtil 工具类来实现 ConfigHelper 助手类, 框架通过 ConfigHelper 类就
* 可以加载用户自定义的配置文件了, 从代码中可以看到, 部分配置项拥有默认值, 当用户没有自定义时将会使用默认配置.
* */
public final class ConfigHelper {
/**
* 加载配置文件的属性
*/
private static final Properties CONFIG_PROPS = PropsUtil.loadProps(ConfigConstant.CONFIG_FILE);
/**
* 获取 JDBC 驱动
*/
public static String getJdbcDriver() {
return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.JDBC_DRIVER);
}
/**
* 获取 JDBC URL
*/
public static String getJdbcUrl() {
return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.JDBC_URL);
}
/**
* 获取 JDBC 用户名
*/
public static String getJdbcUsername() {
return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.JDBC_USERNAME);
}
/**
* 获取 JDBC 密码
*/
public static String getJdbcPassword() {
return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.JDBC_PASSWORD);
}
/**
* 获取应用基础包名
*/
public static String getAppBasePackage() {
return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.APP_BASE_PACKAGE);
}
/**
* 获取应用 JSP 路径
*/
public static String getAppJspPath() {
return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.APP_JSP_PATH, "/WEB-INF/view/");
}
/**
* 获取应用静态资源路径
*/
public static String getAppAssetPath() {
return PropsUtil.getString(CONFIG_PROPS, ConfigConstant.APP_ASSET_PATH, "/asset/");
}
/**
* 根据属性名获取 String 类型的属性值
*/
public static String getString(String key) {
return PropsUtil.getString(CONFIG_PROPS, key);
}
/**
* 根据属性名获取 int 类型的属性值
*/
public static int getInt(String key) {
return PropsUtil.getInt(CONFIG_PROPS, key);
}
/**
* 根据属性名获取 boolean 类型的属性值
*/
public static boolean getBoolean(String key) {
return PropsUtil.getBoolean(CONFIG_PROPS, key);
}
}
复制代码
到这里就可以获取到你所有配置的信息,如果没有配置的也存在了默认值,以此我们可以获取到你想要扫描的包的地址信息。
2、类加载功能
实例化之前,需要先进行类加载,通过指定的包地址,获取下面所有的 Class 类,同样先写出一个 Util类
生成类对象
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;
/*
* ClassUtil 工具类可以通过加载全限定类名得到Class类, 以及获取指定包名下的所有Class类.
* */
public final class ClassUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(ClassUtil.class);
/**
* 获取类加载器
*/
public static ClassLoader getClassLoader() {
return Thread.currentThread().getContextClassLoader();
}
/**
* 加载类
*
* @param className 类名
* @param isInitialized 是否初始化
* @return
*/
public static Class<?> loadClass(String className, boolean isInitialized) {
Class<?> cls;
try {
cls = Class.forName(className, isInitialized, getClassLoader());
} catch (ClassNotFoundException e) {
LOGGER.error("load class failure", e);
throw new RuntimeException(e);
}
return cls;
}
/**
* 加载类(默认将初始化类)
*/
public static Class<?> loadClass(String className) {
return loadClass(className, true);
}
/**
* 获取指定包名下的所有类
*/
public static Set<Class<?>> getClassSet(String packageName) {
Set<Class<?>> classSet = new HashSet<Class<?>>();
try {
// 需要获取的包的名车
Enumeration<URL> urls = getClassLoader().getResources(packageName.replace(".", "/"));
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
if (url != null) {
String protocol = url.getProtocol();
// 如果是一个文件夹
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<Class<?>> classSet, String packagePath, String packageName) {
// 将文件夹下面的以class 结尾的文件进行保存
File[] files = new File(packagePath).listFiles(new FileFilter() {
public boolean accept(File file) {
return (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory();
}
});
// 这里做了以此递归,如果获取到的是文件夹,就在去访问文件夹下面的文件
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<Class<?>> 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());
}
}
复制代码
到这里,指定包路径下面的 类文件都已经被加载成为 类对象,
同理再写一个对应的 helper 去调用 工具类
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 {
/**
* 定义类集合(存放基础包名下的所有类)
*/
private static final Set<Class<?>> CLASS_SET;
static {
//获取基础包名
String basePackage = ConfigHelper.getAppBasePackage();
System.out.println(basePackage);
//获取基础包名下所有类
CLASS_SET = ClassUtil.getClassSet(basePackage);
System.out.println("基础包名下所有类" + CLASS_SET.size());
}
/**
* 获取基础包名下的所有类
*/
public static Set<Class<?>> getClassSet() {
return CLASS_SET;
}
/**
* 获取基础包名下所有 Service 类
*/
public static Set<Class<?>> getServiceClassSet() {
Set<Class<?>> classSet = new HashSet<Class<?>>();
for (Class<?> cls : CLASS_SET) {
if (cls.isAnnotationPresent(Service.class)) {
classSet.add(cls);
}
}
return classSet;
}
/**
* 获取基础包名下所有 Controller 类
*/
public static Set<Class<?>> getControllerClassSet() {
Set<Class<?>> classSet = new HashSet<Class<?>>();
for (Class<?> cls : CLASS_SET) {
if (cls.isAnnotationPresent(Controller.class)) {
classSet.add(cls);
}
}
return classSet;
}
/**
* 获取基础包名下所有 Bean 类(包括:Controller、Service)
*/
public static Set<Class<?>> getBeanClassSet() {
Set<Class<?>> beanClassSet = new HashSet<Class<?>>();
beanClassSet.addAll(getServiceClassSet());
beanClassSet.addAll(getControllerClassSet());
return beanClassSet;
}
/**
* 获取基础包名下某父类的所有子类 或某接口的所有实现类
*/
public static Set<Class<?>> getClassSetBySuper(Class<?> superClass) {
Set<Class<?>> classSet = new HashSet<Class<?>>();
for (Class<?> cls : CLASS_SET) {
//isAssignableFrom() 指 superClass 和 cls 是否相同或 superClass 是否是 cls 的父类/接口
if (superClass.isAssignableFrom(cls) && !superClass.equals(cls)) {
classSet.add(cls);
}
}
return classSet;
}
/**
* 获取基础包名下带有某注解的所有类
*/
public static Set<Class<?>> getClassSetByAnnotation(Class<? extends Annotation> annotationClass) {
Set<Class<?>> classSet = new HashSet<Class<?>>();
for (Class<?> cls : CLASS_SET) {
if (cls.isAnnotationPresent(annotationClass)) {
classSet.add(cls);
}
}
return classSet;
}
}
复制代码
3、实例化service 和 controller 对象
当前的 service 对象和 controller 对象 的类对象已经加载到了 classSet 里面,只需要将其分离出来就可以,在调用类加载器进行加载就可以了,其中 分离代买在 ClassHelper 里面,下面重点讲下 实例化代码
这里的实例化代码会调用 无参构造,所以必须保留无参数构造函数
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相当于一个Spring容器, 拥有应用所有bean的实例
*/
private static final Map<Class<?>, Object> BEAN_MAP = new HashMap<Class<?>, Object>();
static {
//获取应用中的所有bean(service 和 controller 注解的)
Set<Class<?>> beanClassSet = ClassHelper.getBeanClassSet();
System.out.println("beanClassSet的大小" + "**********" + beanClassSet.size());
//将bean实例化, 并放入bean容器中
for (Class<?> beanClass : beanClassSet) {
Object obj = ReflectionUtil.newInstance(beanClass);
System.out.println(obj);
BEAN_MAP.put(beanClass, obj);
}
}
/**
* 获取 Bean 容器
*/
public static Map<Class<?>, Object> getBeanMap() {
return BEAN_MAP;
}
/**
* 获取 Bean 实例
*/
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);
}
/**
* 设置 Bean 实例
*/
public static void setBean(Class<?> cls, Object obj) {
BEAN_MAP.put(cls, obj);
}
}
复制代码
这里的实例化用到了反射
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);
/**
* 创建实例
*/
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;
}
/**
* 创建实例(根据类名)
*/
public static Object newInstance(String className) {
Class<?> cls = ClassUtil.loadClass(className);
return newInstance(cls);
}
/**
* 调用方法
*/
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;
}
/**
* 设置成员变量的值
*/
public static void setField(Object obj, Field field, Object value) {
try {
field.setAccessible(true); //去除私有权限
field.set(obj, value);
} catch (Exception e) {
LOGGER.error("set field failure", e);
throw new RuntimeException(e);
}
}
}
复制代码
到这里已经实现了实现了实例化,但是还么注入属性
4、属性注入
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 {
/**
* 遍历bean容器所有bean的属性, 为所有带@Autowired注解的属性注入实例
*/
static {
//遍历bean容器里的所有bean
Map<Class<?>, Object> beanMap = BeanHelper.getBeanMap();
if (MapUtils.isNotEmpty(beanMap)) {
for (Map.Entry<Class<?>, Object> beanEntry : beanMap.entrySet()) {
//bean的class类
Class<?> beanClass = beanEntry.getKey();
//bean的实例
Object beanInstance = beanEntry.getValue();
//暴力反射获取属性
Field[] beanFields = beanClass.getDeclaredFields();
//遍历bean的属性
if (ArrayUtils.isNotEmpty(beanFields)) {
for (Field beanField : beanFields) {
//判断属性是否带Autowired注解
if (beanField.isAnnotationPresent(Autowired.class)) {
//属性类型
Class<?> beanFieldClass = beanField.getType();
//如果beanFieldClass是接口, 就获取接口对应的实现类
beanFieldClass = findImplementClass(beanFieldClass);
//获取Class类对应的实例
Object beanFieldInstance = beanMap.get(beanFieldClass);
if (beanFieldInstance != null) {
ReflectionUtil.setField(beanInstance, beanField, beanFieldInstance);
}
}
}
}
}
}
}
/**
* 获取接口对应的实现类
*/
public static Class<?> findImplementClass(Class<?> interfaceClass) {
Class<?> implementClass = interfaceClass;
//接口对应的所有实现类
Set<Class<?>> classSetBySuper = ClassHelper.getClassSetBySuper(interfaceClass);
if (CollectionUtils.isNotEmpty(classSetBySuper)) {
//获取第一个实现类
implementClass = classSetBySuper.iterator().next();
}
return implementClass;
}
}
复制代码
到这里就实现了一个完整 bean 容器,这里天然的解决了循环依赖问题
启动测试一下
public final class HelperLoader {
public static void init() {
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());
}
}
复制代码
3.2 MVC 功能
这里需要用tomcat 来启动,可以参考springMVC的架构,
需要存在有 controller ,DispatchServlet
1、controller
首先创建一个 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;
/**
* 用户列表
*
* @return
*/
@RequestMapping(value = "/userList", method = RequestMethod.GET)
public Data getUserList() {
List<User> userList = userService.getAllUser();
return new Data("123213123");
}
}
复制代码
2、请求-处理器构建
可以根据
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;
/**
* 控制器助手类
* 相当于SpringMVC里的映射处理器, 为请求URI设置对应的处理器
*/
public final class ControllerHelper {
/**
* REQUEST_MAP为 "请求-处理器" 的映射
*/
private static final Map<Request, Handler> REQUEST_MAP = new HashMap<Request, Handler>();
static {
//遍历所有Controller类
Set<Class<?>> controllerClassSet = ClassHelper.getControllerClassSet();
if (CollectionUtils.isNotEmpty(controllerClassSet)) {
for (Class<?> controllerClass : controllerClassSet) {
//暴力反射获取所有方法
Method[] methods = controllerClass.getDeclaredMethods();
//遍历方法
if (ArrayUtils.isNotEmpty(methods)) {
for (Method method : methods) {
//判断是否带RequestMapping注解
if (method.isAnnotationPresent(RequestMapping.class)) {
RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
//请求路径
String requestPath = requestMapping.value();
//请求方法
String requestMethod = requestMapping.method().name();
//封装请求和处理器
Request request = new Request(requestMethod, requestPath);
Handler handler = new Handler(controllerClass, method);
REQUEST_MAP.put(request, handler);
}
}
}
}
}
}
/**
* 获取 Handler
*/
public static Handler getHandler(String requestMethod, String requestPath) {
Request request = new Request(requestMethod, requestPath);
return REQUEST_MAP.get(request);
}
}
复制代码
3、前端控制器
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 {
//初始化相关的helper类
HelperLoader.init();
//获取ServletContext对象, 用于注册Servlet
ServletContext servletContext = servletConfig.getServletContext();
//注册处理jsp和静态资源的servlet
registerServlet(servletContext);
}
/**
* DefaultServlet和JspServlet都是由Web容器创建
* org.apache.catalina.servlets.DefaultServlet
* org.apache.jasper.servlet.JspServlet
*/
private void registerServlet(ServletContext servletContext) {
//动态注册处理JSP的Servlet
ServletRegistration jspServlet = servletContext.getServletRegistration("jsp");
jspServlet.addMapping(ConfigHelper.getAppJspPath() + "*");
//动态注册处理静态资源的默认Servlet
ServletRegistration defaultServlet = servletContext.getServletRegistration("default");
defaultServlet.addMapping("/favicon.ico"); //网站头像
defaultServlet.addMapping(ConfigHelper.getAppAssetPath() + "*");
}
@Override
public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String requestMethod = request.getMethod().toUpperCase();
String requestPath = request.getPathInfo();
//这里根据Tomcat的配置路径有两种情况, 一种是 "/userList", 另一种是 "/context地址/userList".
String[] splits = requestPath.split("/");
if (splits.length > 2) {
requestPath = "/" + splits[2];
}
//根据请求获取处理器(这里类似于SpringMVC中的映射处理器)
Handler handler = ControllerHelper.getHandler(requestMethod, requestPath);
if (handler != null) {
Class<?> controllerClass = handler.getControllerClass();
Object controllerBean = BeanHelper.getBean(controllerClass);
//初始化参数
Param param = RequestHelper.createParam(request);
//调用与请求对应的方法(这里类似于SpringMVC中的处理器适配器)
Object result;
Method actionMethod = handler.getControllerMethod();
if (param == null || param.isEmpty()) {
result = ReflectionUtil.invokeMethod(controllerBean, actionMethod);
} else {
result = ReflectionUtil.invokeMethod(controllerBean, actionMethod, param);
}
//跳转页面或返回json数据(这里类似于SpringMVC中的视图解析器)
if (result instanceof View) {
handleViewResult((View) result, request, response);
} else if (result instanceof Data) {
handleDataResult((Data) result, response);
}
}
}
/**
* 跳转页面
*/
private void handleViewResult(View view, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
String path = view.getPath();
if (StringUtils.isNotEmpty(path)) {
if (path.startsWith("/")) { //重定向
response.sendRedirect(request.getContextPath() + path);
} else { //请求转发
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);
}
}
}
/**
* 返回JSON数据
*/
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();
}
}
}
复制代码
请求参数进行封装
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 {
/**
* 获取请求参数
*/
public static Param createParam(HttpServletRequest request) throws IOException {
Map<String, Object> paramMap = new HashMap();
Enumeration<String> paramNames = request.getParameterNames();
//没有参数
if (!paramNames.hasMoreElements()) {
return null;
}
//get和post参数都能获取到
while (paramNames.hasMoreElements()) {
String fieldName = paramNames.nextElement();
String fieldValue = request.getParameter(fieldName);
paramMap.put(fieldName, fieldValue);
}
return new Param(paramMap);
}
}
复制代码
<%@ 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>用户信息</title>
</head>
<body>
<h1>用户信息</h1>
<table>
<tr>
<th>用户id</th>
<th>名称</th>
<th>年龄</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}">详情</a>
<a href="${BASE}/userEdit?id=${userinfo.id}">编辑</a>
</td>
</tr>
</c:forEach>
</table>
</body>
</html>
复制代码
启动测试
3.3 AOP 功能
前面已经实现了 ioc 和 mvc 功能,下面开始实现 AOP 功能,采用的是链式调用的方案,将多个代理顺序调用
这里的代码会稍微比较复杂,先简单讲述下大概的思路,不然不熟悉aop调用的估计看的一头雾水
- 首先会创建切面注解(Aspect),用于表示这是一个切面,(简单)
- 创建 proxy 和 AspectProxy ,前者用于实现链式调用,后者用于定义前置后置回滚的执行时机,是一个abstract类,其中相关方法等待填充(简单)
- 创建一个传统切面形式,本文创建一个(EfficientAspect),继承 AspectProxy (简单)
到这里已经定义好了 切面和切入点,下面进行需要做的就是把切面 切入到 你的被代理对象中,用 Aophelper
- 首先在扫描包的时候,需要扫描到所有的 AspectProxy 实现类,这些是切面
- 需要保存 切面-》被代理, 被代理类-》切面类 的两个hash
- 而后进行 代理对象的创建 这里采用的是CGLIB
- 然后再进行调用目标方法的时候,会切入到 Cglib的 intercept ,这里采用的是链式调用,通过被代理对象-》切面类,可以成功构建出一条链,然后根据链表的长度进行递归调用,如果链表长度等于0,就执行自身方法
-这里有个注意点就是 ,CGLIB 获取的代理对象,不会包含被代理对象的成员变量,所有调用自身方法的时候,需要获取到被代理对象,然后执行
看不懂的一步步调试把,这个第一次看比较绕
1、创建切面注解
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() default "";
/**
* 类名
*/
String cls() default "";
}
复制代码
2、代理类的创建
package lz.proxy;
public interface Proxy {
/**
* 执行链式代理
* 所谓链式代理, 就是说, 可将多个代理通过一条链子串起来, 一个个地去执行, 执行顺序取决于添加到链上的先后顺序
*/
Object doProxy(ProxyChain proxyChain) throws Throwable;
}
复制代码
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;
}
/**
* 开始增强
*/
public void begin() {
}
/**
* 切入点判断
*/
public boolean intercept(Method method, Object[] params) throws Throwable {
return true;
}
/**
* 前置增强
*/
public void before(Method method, Object[] params) throws Throwable {
}
/**
* 后置增强
*/
public void after(Method method, Object[] params) throws Throwable {
}
/**
* 异常增强
*/
public void error(Method method, Object[] params, Throwable e) {
}
/**
* 最终增强
*/
public void end() {
}
}
复制代码
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;
/**
* 切入点判断
*/
@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 -----------");
}
}
复制代码
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 final Class<?> targetClass; //目标类
private final Object targetObject; //目标对象
private final Method targetMethod; //目标方法
private final MethodProxy methodProxy; //方法代理
private final Object[] methodParams; //方法参数
private List<Proxy> proxyList = new ArrayList<>(); //代理列表
private int proxyIndex = 0; //代理索引
public ProxyChain(Class<?> targetClass, Object targetObject, Method targetMethod, MethodProxy methodProxy, Object[] methodParams, List<Proxy> proxyList) {
this.targetClass = targetClass;
this.targetObject = targetObject;
this.targetMethod = targetMethod;
this.methodProxy = methodProxy;
this.methodParams = methodParams;
this.proxyList = proxyList;
}
public Object[] getMethodParams() {
return methodParams;
}
public Class<?> getTargetClass() {
return targetClass;
}
public Method getTargetMethod() {
return targetMethod;
}
/**
* 递归执行
*/
public Object doProxyChain() throws Throwable {
Object methodResult;
if (proxyIndex < proxyList.size()) {
//执行增强方法
methodResult = proxyList.get(proxyIndex++).doProxy(this);
} else {
//目标方法最后执行且只执行一次
Object bean_proxy = BeanHelper.getBean_proxy(targetClass);
methodResult = targetMethod.invoke(bean_proxy, methodParams);
}
return methodResult;
}
}
复制代码
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 {
/**
* 输入一个目标类和一组Proxy接口实现, 输出一个代理对象
*/
@SuppressWarnings("unchecked")
public static <T> T createProxy(final Class<?> targetClass, final List<Proxy> proxyList) {
return (T) Enhancer.create(targetClass, new MethodInterceptor() {
/**
* 代理方法, 每次调用目标方法时都会先创建一个 ProxyChain 对象, 然后调用该对象的 doProxyChain() 方法.
*/
@Override
// 目标方法调用的时候, 会自动进入到这个方法里面,这个方法 创建了一个 chain ,通过chain 进行调用
public Object intercept(Object targetObject, Method targetMethod, Object[] methodParams, MethodProxy methodProxy) throws Throwable {
return new ProxyChain(targetClass, targetObject, targetMethod, methodProxy, methodParams, proxyList).doProxyChain();
}
});
}
}
复制代码
3、AOPhelper 用于载入 AOP 功能
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 {
//切面类-目标类集合的映射
Map<Class<?>, Set<Class<?>>> aspectMap = createAspectMap();
//目标类-切面对象列表的映射
Map<Class<?>, List<Proxy>> targetMap = createTargetMap(aspectMap);
//把切面对象织入到目标类中, 创建代理对象
for (Map.Entry<Class<?>, List<Proxy>> targetEntry : targetMap.entrySet()) {
Class<?> targetClass = targetEntry.getKey();
List<Proxy> proxyList = targetEntry.getValue();
Object proxy = ProxyFactory.createProxy(targetClass, proxyList);
//覆盖Bean容器里目标类对应的实例, 下次从Bean容器获取的就是代理对象了
BeanHelper.setBean_proxy(targetClass, BeanHelper.getBean(targetClass));
// 用于获取被代理对象,否则在代理对象中无法获取成员变量
BeanHelper.setBean(targetClass, proxy);
}
} catch (Exception e) {
LOGGER.error("aop failure", e);
}
}
/**
* 获取切面类-目标类集合的映射
*/
private static Map<Class<?>, Set<Class<?>>> createAspectMap() throws Exception {
Map<Class<?>, Set<Class<?>>> aspectMap = new HashMap<Class<?>, Set<Class<?>>>();
addAspectProxy(aspectMap);
addTransactionProxy(aspectMap);
return aspectMap;
}
/**
* 获取普通切面类-目标类集合的映射
*/
private static void addAspectProxy(Map<Class<?>, Set<Class<?>>> aspectMap) throws Exception {
//所有实现了AspectProxy抽象类的切面
Set<Class<?>> aspectClassSet = ClassHelper.getClassSetBySuper(AspectProxy.class);
for (Class<?> aspectClass : aspectClassSet) {
if (aspectClass.isAnnotationPresent(Aspect.class)) {
Aspect aspect = aspectClass.getAnnotation(Aspect.class);
//与该切面对应的目标类集合
Set<Class<?>> targetClassSet = createTargetClassSet(aspect);
aspectMap.put(aspectClass, targetClassSet);
}
}
}
/**
* 获取事务切面类-目标类集合的映射
*/
private static void addTransactionProxy(Map<Class<?>, Set<Class<?>>> aspectMap) {
Set<Class<?>> serviceClassSet = ClassHelper.getClassSetByAnnotation(Service.class);
aspectMap.put(TransactionProxy.class, serviceClassSet);
}
/**
* 根据@Aspect定义的包名和类名去获取对应的目标类集合
*/
private static Set<Class<?>> createTargetClassSet(Aspect aspect) throws Exception {
Set<Class<?>> targetClassSet = new HashSet<Class<?>>();
// 包名
String pkg = aspect.pkg();
// 类名
String cls = aspect.cls();
// 如果包名与类名均不为空,则添加指定类
if (!pkg.equals("") && !cls.equals("")) {
targetClassSet.add(Class.forName(pkg + "." + cls));
} else if (!pkg.equals("")) {
// 如果包名不为空, 类名为空, 则添加该包名下所有类
targetClassSet.addAll(ClassUtil.getClassSet(pkg));
}
return targetClassSet;
}
/**
* 将切面类-目标类集合的映射关系 转化为 目标类-切面对象列表的映射关系,这里的切面可能存在多个
*/
private static Map<Class<?>, List<Proxy>> createTargetMap(Map<Class<?>, Set<Class<?>>> aspectMap) throws Exception {
Map<Class<?>, List<Proxy>> targetMap = new HashMap<Class<?>, List<Proxy>>();
for (Map.Entry<Class<?>, Set<Class<?>>> proxyEntry : aspectMap.entrySet()) {
//切面类
Class<?> aspectClass = proxyEntry.getKey();
//目标类集合
Set<Class<?>> targetClassSet = proxyEntry.getValue();
//创建目标类-切面对象列表的映射关系
for (Class<?> targetClass : targetClassSet) {
//切面对象
Proxy aspect = (Proxy) aspectClass.newInstance();
if (targetMap.containsKey(targetClass)) {
targetMap.get(targetClass).add(aspect);
} else {
//切面对象列表
List<Proxy> aspectList = new ArrayList<Proxy>();
aspectList.add(aspect);
targetMap.put(targetClass, aspectList);
}
}
}
return targetMap;
}
}
复制代码
doProxyChain -> 选择链表的一个对象
doProxy -> 开始调用所选择对象(切面类)中的代理方法 -> 执行到 result = proxyChain.doProxyChain(); 就是前置通知执行结束,如果是静态代理就应该是执行当前方法,但它选择的是再去寻找调用链
doProxyChain -> 根据 index 索引选择链表的下一个对象
如果只有一个切面,说明index = 0, 就可以执行 被代理对象的方法呢
如果有多个切面,说明index != 0 就需要在回到 doProxy 里面
当所有的前置通知执行完毕,说明 index = 0,这时候就可以执行被代理对象的方法了,最后进行返回(这里是一个递归操作!!!有几个切面,递归几层那种意思)
到这里,aop 功能就已经实现了,需要注意的是多个切面同时代理一个对象的时候,顺序是谁先扫描到,压入list,谁先调用,这里细化的话,可以添加一个参数pripority进行排序
3.4 事务
spring事务是基于数据库事务的支持,事务说简单点就是把 自动提交关闭,然后一步步自己做操作,加入一个 @transaction 就是一个代理,底层就是AOP。
这里我们选择的是 将所有的加入@servie 的service 层进行以此代理,再执行的时候进行判断,该方法是否加入了 @transaction ,如果加入了 @transaction,就采用事务的方法进行调用,如果没有加,就正常调用调用就可以了
1、数据库
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;
/**
* 数据库操作助手类
*
* @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());
}
/**
* 获取数据源
*/
public static DataSource getDataSource() {
return DATA_SOURCE;
}
/**
* 获取数据库连接
*/
public static Connection getConnection() {
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;
}
/**
* 开启事务
*/
public static void beginTransaction() {
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);
}
}
}
/**
* 提交事务
*/
public static void commitTransaction() {
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();
}
}
}
/**
* 回滚事务
*/
public static void rollbackTransaction() {
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();
}
}
}
/**
* 查询实体
*/
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;
}
/**
* 查询实体列表
*/
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;
}
/**
* 执行更新语句(包括: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;
}
/**
* 插入实体
*/
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;
}
/**
* 更新实体
*/
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;
}
/**
* 删除实体
*/
public static <T> boolean deleteEntity(Class<T> entityClass, long id) {
String sql = "DELETE FROM " + entityClass.getSimpleName() + " WHERE id = ?";
return update(sql, id) == 1;
}
}
复制代码
2、事务代理
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();
//加了@Transactional注解的方法要做事务处理
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");
throw e;
}
} else {
result = proxyChain.doProxyChain();
}
return result;
}
}
复制代码
3、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 {
/**
* 获取所有用户
*/
@Override
public List<User> getAllUser() {
String sql = "SELECT * FROM user";
return DatabaseHelper.queryEntityList(User.class, sql);
}
/**
* 根据id获取用户信息
*/
@Override
public User GetUserInfoById(Integer id) {
String sql = "SELECT * FROM user WHERE id = ?";
return DatabaseHelper.queryEntity(User.class, sql, id);
}
/**
* 修改用户信息
*/
@Transactional
@Override
public boolean updateUser(int id, Map<String, Object> fieldMap) {
return DatabaseHelper.updateEntity(User.class, id, fieldMap);
}
}
复制代码
4、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;
/**
* 用户列表
*
* @return
*/
@RequestMapping(value = "/userList", method = RequestMethod.GET)
public View getUserList() {
List<User> userList = userService.getAllUser();
return new View("index.jsp").addModel("userList", userList);
}
/**
* 用户详情
*
* @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.");
}
}
复制代码
通过 tomcat 进行测试
4、总结
本文实现了 spring 的 IOC/AOP/MVC/事务功能,主要代码是参考了这里面的,并对其进行了适当的修改(里面代码有一些小错误),如果想起tomcat 搭建麻烦,自己写几个测试类测试下就好了,主要的困难点在 AOP 的链式调用那边,其他的很好理解
回忆当初学spring源码的AOP 和 IOC 点进去一个方法,就和点了火药桶一样