文章模仿Spring对于IOC和AOP的处理思路,自定义注解实现了对bean的扫描、依赖注入和事务管理。实现思路大体如下:
<dependency>
<groupId>org.reflectionsgroupId>
<artifactId>reflectionsartifactId>
<version>0.9.11version>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-lang3artifactId>
<version>3.6version>
dependency>
自定义了三个注解@Service,@Autowired和@Transactional,分别用于对象创建,属性注入和事务管理。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {
//要注入的对象名称
String value() default "";
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Transactional {
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}
创建容器的入口WebApplicationContext,定义ConcurrentHashMap做为单例池,存放扫描过程中创建的对象,同时定义了扫描的类路径ROOT。
public class WebApplicationContext {
private static final String ROOT = "com.keduw";
private static final ConcurrentHashMap<String, Object> beanMap = new ConcurrentHashMap<>(256);
}
扫描指定路径下的所有文件,获取添加了@Service的所有类信息,通过反射创建指定的对象存到单例池中。
public static void initBeanFactory() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Reflections reflections = new Reflections(ROOT);
Set<Class<?>> serviceClazz = reflections.getTypesAnnotatedWith(Service.class);
for (Class<?> clazz : serviceClazz) {
// 反射创建指定对象
Object o = clazz.getDeclaredConstructor().newInstance();
// 有指定value则将value作为key
// 没有则将对应的类名首字母转为小写后作为key
Service annotation = clazz.getAnnotation(Service.class);
String key = annotation.value();
if(StringUtils.isEmpty(key)){
String[] split = clazz.getName().split("\\.");
key = CharsetUtils.toLowerCase(split[split.length - 1]);
}
registerBean(key, o);
}
}
遍历单例池,获取所有的对象和类信息,通过判断类中的属性是否添加了@Autowired注解,存在则获取依赖属性对应的名称作为key,从单例池中获取到指定对象,通过反射调用属性的set方法实现注入。
同时处理@Transactional注解,如果类中存在指定注解,则判断是否实现接口,如果实现接口则调用JDK动态代理创建代理对象,没有则调用Cglib动态代理创建对象。
for (Map.Entry<String, Object> bean : beanMap.entrySet()) {
Object o = bean.getValue();
Class clazz = o.getClass();
// 获取所有属性
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(Autowired.class)) {
// 获取依赖属性对应的名称
String[] names = field.getType().getName().split("\\.");
String name = CharsetUtils.toLowerCase(names[names.length - 1]);
// 给指定属性赋值
Field declaredField = clazz.getDeclaredField(name);
declaredField.setAccessible(true);
declaredField.set(o, beanMap.get(name));
}
}
// 处理Transactional注解
if(clazz.isAnnotationPresent(Transactional.class)){
ProxyFactory proxyFactory = (ProxyFactory) getBean("proxyFactory");
//判断对象是否实现接口
Class[] interfaces = clazz.getInterfaces();
o = (interfaces != null && interfaces.length > 0) ? proxyFactory.getJdkProxy(o) : proxyFactory.getCglibProxy(o);
}
registerBean(bean.getKey(), o);
}
代理中处理的事情就相对比较简单,主要是在方法执行前开启事务,在方法执行完后提交事务,如果出现异常则回滚事务。
/**
* Jdk动态代理
* @param obj
* @return
*/
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 {
Object result = null;
try{
transactionManager.beginTransaction();
result = method.invoke(obj, args);
transactionManager.commit();
}catch (Exception e) {
e.printStackTrace();
transactionManager.rollback();
throw e;
}
return result;
}
});
}
/**
* cglib动态代理
* @param obj
* @return
*/
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 {
Object result = null;
try{
transactionManager.beginTransaction();
result = method.invoke(obj, objects);
transactionManager.commit();
}catch (Exception e) {
e.printStackTrace();
transactionManager.rollback();
throw e;
}
return result;
}
});
}
到这里基本就实现了对象的扫描、依赖注入和事务控制,整体思路跟Spring的设计是类似的。文章中只显示了核心代码,完整的代码已提交,感兴趣的可以看看。(地址:https://gitee.com/lveex/kd-container.git)