作业要求:
学员自定义 @Service、@Autowired、@Transactional 注解类,完成基于注解的 IoC 容器(Bean 对象创建及依赖注入维护)和声明式事务控制,写到转账工程中,并且可以实现转账成功和转账异常时事务回滚。
注意考虑以下情况:
-
注解有无
value属性值【@service(value="") @Repository(value="")】 -
service层是否实现接口的情况【 JDK 还是 CGLib 】
分析:
-
按照课程里面的项目,实现以 XML 形式的 IoC 容器,同时实现 AOP 功能。这里多说一句, XML 解析我是使用 DOM 搭配 XPath 实现的,与 MyBatis 中类似,但是没有进一步封装。
-
编写注解,替换注解中对应的标签,并且修改其解析代码,实现作业要求。
核心部分:
其实这个作业最核心的部分就是扫描对应包路径下的所有类。在 Java 当中,目前我所知几种实现方式:
- 纯 Java 方式,利用文件系统。
- 使用 Spring 中的扫描工具类
MetadataReaderFactory - 使用外部反射工具,
reflections
作业中,我是使用的 Spring 的扫描工具类实现对应功能,这种方式更加贴近 Spring 源码。纯 Java 的方式实现比较复杂,而且有可能出现问题。而使用外部反射工具,这种方式非常简单,其内部封装了许多方便快捷的方法。
代码:
注解部分:
-
Autowired
package cn.worstone.annotation; import java.lang.annotation.*; @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Autowired { } -
Component
package cn.worstone.annotation; import java.lang.annotation.*; @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Component { String value() default ""; } -
Repository
package cn.worstone.annotation; import java.lang.annotation.*; @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Repository { String value() default ""; } -
Service
package cn.worstone.annotation; import java.lang.annotation.*; @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Repository { String value() default ""; } -
Transactional
package cn.worstone.annotation; import java.lang.annotation.*; @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Transactional { }
代码修改部分:
-
TransferServlet
// private ProxyFactory proxyFactory = (ProxyFactory) XMLBeanFactory.getBean("proxyFactory"); // private AccountService accountService = (AccountService) proxyFactory.getJdkProxy(XMLBeanFactory.getBean("accountService")); private AccountService accountService = (AccountService) AnnotationBeanFactory.getBean("accountService");这里说明一下,在 Spring 中,因为其通过 SpringMVC 中的 DispatchServlet 对所有请求进行拦截,然后统一处理分发,所以可以使用对应的 Controller 作为每次请求的入口。而这实在是太复杂了,实在是没有时间精力。/(ㄒoㄒ)/~~。所以这里只能和老师一样直接写死入口,其次,因为事务修改为注解实现的声明式事务,所以这块不需要显式获取代理对象,直接从 BeanFactory 中获取 Bean 即可。
-
AnnotationBeanFactory
package cn.worstone.factory; import cn.worstone.annotation.*; import cn.worstone.service.AccountService; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.classreading.SimpleMetadataReaderFactory; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; public class AnnotationBeanFactory { private static Map<String, Object> beansMap = new HashMap<>(); static { beansMap.putAll(XMLBeanFactory.getBeansMap()); String packageName = "cn.worstone"; try { ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); Resource[] resources = resourcePatternResolver.getResources(packageHandler(packageName)); MetadataReaderFactory metadata = new SimpleMetadataReaderFactory(); for (Resource resource : resources) { MetadataReader metadataReader = metadata.getMetadataReader(resource); String className = metadataReader.getClassMetadata().getClassName(); Class<?> aClass = Class.forName(className); Map<String, Object> componentMap = componentAnnotation(aClass); if ((boolean) componentMap.get("isComponent")) { className = getName(aClass, (String) componentMap.get("value")); Object o = aClass.getDeclaredConstructor().newInstance(); beansMap.put(className, o); } } Map<String, Object> transactionMap = new HashMap<>(); for (String s : beansMap.keySet()) { Object bean = beansMap.get(s); // 处理 Autowired 注解 Field[] fields = bean.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); if (field.getAnnotation(Autowired.class) != null) { Object o = getByType(field.getType()); // 正常来说需要对基础类型进行判断 if (o == null) { throw new RuntimeException("Not have the type bean: " + field.getType()); } field.set(bean, o); } } beansMap.put(s, bean); // 处理 Transactional 注解 Transactional transactional = bean.getClass().getAnnotation(Transactional.class); if (transactional != null) { transactionMap.put(s, bean); } } for (String s : transactionMap.keySet()) { Object bean = beansMap.get(s); Class<?>[] interfaces = bean.getClass().getInterfaces(); ProxyFactory proxyFactory = (ProxyFactory) beansMap.get("proxyFactory"); Object proxy = null; if (proxyFactory == null) { throw new RuntimeException("Proxy Factory is null"); } if (interfaces.length == 0) { proxy = proxyFactory.getJdkProxy(bean); } else { proxy = proxyFactory.getCglibProxy(bean); } beansMap.put(s, proxy); } } catch (Exception e) { e.printStackTrace(); } } public static Object getBean(String id) { return beansMap.get(id); } private static Map<String, Object> componentAnnotation(Class<?> aClass) { Map<String, Object> result = new HashMap<>(4); result.put("isComponent", false); result.put("value", ""); if (aClass.isAnnotation()) return result; Component component = aClass.getAnnotation(Component.class); Service service = aClass.getAnnotation(Service.class); Repository repository = aClass.getAnnotation(Repository.class); if (component != null) { result.put("isComponent", true); result.put("value", component.value()); return result; } if (service != null) { result.put("isComponent", true); result.put("value", service.value()); return result; } if (repository != null) { result.put("isComponent", true); result.put("value", repository.value()); return result; } return result; } private static String toLowerCaseFirstChar(String word) { if (Character.isUpperCase(word.charAt(0))) { return word.replace(word.charAt(0), (char) (word.charAt(0) + 32)); } return word; } private static String getName(Class<?> aClass, String value) { if (!"".equals(value)) return value; return toLowerCaseFirstChar(aClass.getSimpleName()); } private static String packageHandler(String packageName) { packageName = packageName.replaceAll("\\.", "/"); if (!packageName.endsWith("/")) { packageName += "/"; } return packageName + "**/*.class"; } private static Object getByType(Class<?> aClass) { Object result = null; for (String s : beansMap.keySet()) { Object o = beansMap.get(s); if (aClass.isAssignableFrom(o.getClass())) { if (result != null) { throw new RuntimeException("Finding the type bean is not only one: " + aClass.getName()); } result = o; } } return result; } public static Map<String, Object> getBeansMap() { return beansMap; } }这个类为了与 XML 解析进行区分,所以重新创建了一个 BeanFactory,同时,XML 解析的 BeanFactory 让我修改为 XMLBeanFactory。
-
ProxyFactory
package cn.worstone.factory; import cn.worstone.annotation.Autowired; import cn.worstone.annotation.Component; import cn.worstone.utils.TransactionManager; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.sql.SQLException; @Component public class ProxyFactory { @Autowired private TransactionManager transactionManager; public void setTransactionManager(TransactionManager transactionManager) { this.transactionManager = transactionManager; } // JDK 动态代理 public Object getJdkProxy(Object obj) { // 获取代理对象 return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return getObject(method, args, obj); } }); } // CGLib 动态代理 public Object getCglibProxy(Object obj) { return Enhancer.create(obj.getClass(), new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { return getObject(method, objects, obj); } }); } private Object getObject(Method method, Object[] args, Object obj) throws SQLException, IllegalAccessException, InvocationTargetException { Object result = null; try { // 开启事务 (关闭事务的自动提交) transactionManager.beginTransaction(); result = method.invoke(obj, args); // 提交事务 transactionManager.commit(); } catch (Exception e) { e.printStackTrace(); // 回滚事务 transactionManager.rollback(); // 抛出异常便于上层 Servlet 捕获 throw e; } return result; } }这里将两个动态代理方法内部获取代理对象以及织入事务部分的代码抽象了出来,因为两者代码几乎一致。
目前存在的缺点:(对比 Spring)
- Autowired 注解目前功能实现比较单一,在 Spring 中可以放置在构造方法上。
- Transactional 注解功能实现比较单一,在 Spring 中有一整套非常完成的属性设置。
- 这里直接写死解析的包路径,在 Spring 中有相关的注解。
- 入口 Servlet 直接写死,在 Spring 是通过在 web.xml 中配置配置信息,将其加载到 ApplicationContext 中。
问题:
- 使用 Tomcat 的 Maven 插件没办法运行。
- 异常信息没有在控制台打印。
- Web 项目中,如何定义 Spring 入口
- Tomcat 没有找到类
- Spring 源码构建
- 如何通过注解的 Class 获取注解中的属性值
- 为什么无需要配置 Servlet 就可以直接使用
本文介绍了如何实现基于注解的IoC容器和声明式事务控制,包括自定义注解、类扫描、以及核心组件如Autowired、Component、Repository、Service和Transactional。文章对比了Spring的实现方式,指出了当前实现的不足,如@Autowired和@Transactional功能的局限性,以及固定包路径扫描和Servlet入口写死的问题。此外,还提到了在Tomcat运行时遇到的问题和Spring源码的相关知识点。

被折叠的 条评论
为什么被折叠?



