Spring实践笔记-手写简易的Spring第三篇之注解

文章内容输出来源:拉勾教育Java高薪训练营

相关文章

问题

上两篇中实现了IOC容器管理Bean和事务管理的AOP实现。但使用上还有些繁琐。

  • 问题一
    如下面在单元测试类中的获取代理对象实现事务的代码:
private ProxyFactory proxyFactory = (ProxyFactory) BeanFactory.getBean("proxyFactory");
private TransferService transferService = (TransferService)proxyFactory.getJdkProxy(BeanFactory.getBean("transferService"));

它需要显式的去代理工厂获取对象,再做一个强制的类型转换。当业务比较大的时候,就比较繁琐,而且效率低下了。

  • 问题二
    项目的业务比较大,那创建类就会比较多,每个类都要在beans.xml中声明一个节点,那长期以来这个配置文件就越来越长,更不好维护。

问题思路

在Spring中,它可以通过@Service,@Repository,@Component来声明Bean,通过@Transactional来声明事务,通过@Autowired来进行依赖注入

接下来跟着Spring的这些思路,手写一下注解实现。

注解实现

1. 创建注解类
  • Service/Repository/Component
    • 都表示需要IoC容器管理的类,不同的是Service表示业务类,Repository表示数据访问类,Component表示其他组件类
    • 可在类上进行声明
    • 拥有属性value, 如果要自定义bean名称,则可以为此属性赋值
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Service {
    String value() default "";
}


@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Component {
    String value() default "";
}

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Repository {
    String value() default "";
}
  • Transactional
    • 可在类或者方法上进行声明
    • 没有属性
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Transactional {}
  • Autowired
    • 表示需要依赖注入
    • 在类的属性进行声明,注入时需要属性实现setter方法
    • 没有属性
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {}
2. 创建Bean信息类BeanDefinition
public class BeanDefinition {
    //Bean对象
    private Object object;
	//是否配置了事务.1.类配置了事务注解.2.类没有配置事务注解,但是方法上配置了事务注解
    private Boolean hasTransation;
    //是否拥有接口。存在接口,使用JDK动态代理。不存在接口,使用CGLIB动态代理 
    private Boolean hasInterface;
}
3. 在配置文件中配置上需要进行扫描放到IOC容器中管理的类所在的包名package
<beans>
    <!--扫描包下所有注解的类型-->
    <component-scan base-package="com.yyh.service,com.yyh.dao,com.yyh.utils"/>
</beans>
4. 在BeanFactory的初始化加载Bean方法中增加注解解析处理

1) 解析获取所有的包名
2) 扫描包下的带注解的类
3)获取Bean名称。有返回beanName,则表示有相应的注解,需要去进行实例化操作
- 判断注解上是否有自定义名称,如果有则使用自定义名称
- 如果没有,则判断是否实现了单一接口,如果有则使用接口名称
- 如果没有,则使用类名
- 最后对Bean名称进行处理,首字母小写
4)创建Bean,并放入单例池
- 类标记了事务注解、或者方法标记了事务注解,则标记相应的Bean为配置了事务
- 类实现了接口,则标记相应的Bean为配置了接口
5)获取标识了注解Autowired的属性数据
6)对属性进行注入接口

private static void loadBeansByAnnoation(List<Element> componentScanList) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException {
    //1. 解析获取所有的包名
    Set<String> basePackages = getBasePackages(componentScanList);

    if(null == basePackages) {
        return;
    }

    Map<String, List<String>> fieldMap = new HashMap<>();

    for (String basePackage : basePackages) {
        //2. 扫描包下的带注解的类
        Set<String> classNames = PackageUtils.scanClassNames(basePackage);

        for (String className : classNames) {
            Class<?> aClass = Class.forName(className);

            //3. 获取Bean名称。有返回beanName,则表示有相应的注解,需要去进行实例化操作
            String beanName = getBeanName(aClass);

            if(null != beanName) {
                //4. 创建Bean,并放入单例池
                createBean(beanName, aClass);

                //5. 获取标识了注解Autowired的属性数据
                fieldMap.put(beanName, getAutoWiredFields(aClass));
            }
        }
    }

    //6. 对属性进行注入
    injectFields(fieldMap);
}

/**
 * 创建Bean实体
 * @param beanName
 * @param aClass
 * @throws IllegalAccessException
 * @throws InstantiationException
 */
private static void createBean(String beanName, Class<?> aClass) throws IllegalAccessException, InstantiationException {
    Object o = aClass.newInstance();

    //检查是否拥有事务
    boolean hasTransaction = checkHasTransaction(aClass);
    //检查 量澡拥有接口
    boolean hasInterface = null != aClass.getInterfaces() && aClass.getInterfaces().length > 0;

    BeanDefinition beanDefinition = new BeanDefinition();
    beanDefinition.setObject(o);
    beanDefinition.setHasTransation(hasTransaction);
    beanDefinition.setHasInterface(hasInterface);
    beanMap.put(beanName, beanDefinition);
}

/**
 * 为属性注入
 * @param fieldMap
 */
private static void injectFields(Map<String, List<String>> fieldMap) {
    fieldMap.forEach((beanName, fields) -> {
        Object o =  beanMap.get(beanName).getObject();
        Method[] methods = o.getClass().getMethods();

        for (String field : fields) {
            for (int j = 0; j < methods.length; j++) {
                Method method = methods[j];
                if(method.getName().equalsIgnoreCase("set" + field)) {
                    try {
                        method.invoke(o, getBean(field));

                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    });
}

/**
 * 获取类中标记了注解的属性
 * @param aClass
 * @return
 */
private static List<String> getAutoWiredFields(Class<?> aClass) {
    //获取到标记了Autowired的注解属性

    List<String> fieldNames = new ArrayList<>();

    Field[] declaredFields = aClass.getDeclaredFields();

    for (Field declaredField : declaredFields) {
        Autowired autoWiredAnno = declaredField.getAnnotation(Autowired.class);

        if(null != autoWiredAnno) {
            String fieldName = declaredField.getName();
            fieldNames.add(fieldName);
        }
    }

    return fieldNames;
}

/**
 * 解析获取所有的包名
 * @param componentScanList
 * @return
 */
private static Set<String> getBasePackages(List<Element> componentScanList) {
    if(null == componentScanList || componentScanList.size() == 0) {
        return null;
    }

    Set<String> basePackages = new HashSet<>();

    for (int i = 0; i < componentScanList.size(); i++) {
        Element element = componentScanList.get(i);

        String basePackageAttr = element.attributeValue("base-package");

        if(null != basePackageAttr && basePackageAttr.length() > 0) {
            for (String p : basePackageAttr.split(",")) {
                basePackages.add(p);
            }
        }
    }

    return basePackages;
}


/**
 * 检查是否有事务配置
 * @param aClass
 * @return
 */
private static boolean checkHasTransaction(Class<?> aClass) {
    //判断方法上是否标识了注解
    Method[] methods = aClass.getMethods();

    if(null != methods) {
        for (Method method : methods) {
            if(null != method.getAnnotation(Transactional.class)) {
                return true;
            }

        }
    }

    //判断类上是否标识了注解
    return null != aClass.getAnnotation(Transactional.class);
}
5. 代理工厂ProxyFactory增加CGLIB动态代理生成对象方法
  • 引入依赖
<!--引入cglib依赖包-->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.1_2</version>
</dependency>
  • 增加的代理方法
/**
 * 使用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();
                // 抛出异常便于上层servlet捕获
                throw e;
            }
            return result;
        }
    });
}
6. 创建Bean工厂单例工具类BeanFactoryUtils

1)调用Bean工厂的初始化加载Bean方法
2)提供getBean方法(支持泛型)
- 从单例池中获取Bean信息
- 如果Bean是配置了事务,则调用动态代理工厂进行处理
- 根据是否配置了接口,使用JDK动态代理或者Cglib动态代理

 public <T> T getBean(String beanName, Class<T> type) {
        BeanDefinition beanDefinition = BeanFactory.getBeanDefinition(beanName);

        //判断是否配置为事务
        if(beanDefinition.getHasTransation()) {
            //根据是否存在接口使用不同的代理
            Object object = beanDefinition.getHasInterface() ?
                    proxyFactory.getJdkProxy(beanDefinition.getObject()) : proxyFactory.getCglibProxy(beanDefinition.getObject());

            return type.cast(object);

        }else {
            return type.cast(beanDefinition.getObject());
        }

    }
7. 在相关的业务类、工具类上声明下注解或者依赖注入的注解
  • 修改DAO类
@Repository
public class AccountDaoImpl implements AccountDao {
    @Autowired
    private ConnectionUtils connectionUtils;  
  • 修改业务类,自定义bean名称为transferBusiness。并在transfer业务方法上加上事务注解
@Service("transferBusiness")
public class TransferServiceImpl implements TransferService {
    @Autowired
    private AccountDao accountDao;


    @Override
    @Transactional
    public void transfer(String fromCardNumber, String toCardNumber, int money) throws SQLException {
        AccountEntity fromAccount = accountDao.selectOneByCardNumber(fromCardNumber);
        AccountEntity toAccount = accountDao.selectOneByCardNumber(toCardNumber);

        accountDao.updateMoneyByCardNumber(fromCardNumber, fromAccount.getMoney() - money);
        //抛出一个异常
        int a = 123 / 0;
        accountDao.updateMoneyByCardNumber(toCardNumber, toAccount.getMoney() + money);

    }
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
}
  • 修改数据库连接工具类
@Component
public class ConnectionUtils {

  • 修改代理工厂类
@Component
public class ProxyFactory {
    @Autowired
    private TransactionManager transactionManager;
}    
  • 修改事务管理类
@Component
public class TransactionManager {
    @Autowired
    private ConnectionUtils connectionUtils;

  • 修改beans.xml,去掉多余的bean节点,保留配置注解的扫描包
<beans>
    <component-scan base-package="com.yyh.service,com.yyh.dao,com.yyh.utils"/>
</beans>
8. 单元测试
public class TransferTest {
    @Test
    public void testTransfer() throws SQLException {
        //根据自定义的名称,使用BeanFactory工具类去拿到业务对象
        TransferService transferService = BeanFactoryUtils.getInstance().getBean("transferBusiness", TransferService.class);
        //执行转账
        //为测试事务是否生效,可以在这个方法内部的两个更新方法中间添加上抛出异常的方法
        transferService.transfer("6029621011000", "6029621011001", 100);
    }
}

项目代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值