@TOC
前言
上一篇点击查看应用xml来实现自定义IOC以及依赖注入关系保护,动静代理,以及事务操作;
这次应用注解来实现IOC以及依赖关系保护
步骤以及思路剖析
基于xml实现形式时,仅仅只须要在xml外面配置好bean的id以及bean的权限定命名,而后反射实例化对象,最初退出到ioc容器中
依赖注入时候仅仅须要获取property
标签以及父级标签,依据property
名从ioc容器中获取到须要注入的bean示例即可;
如果是基于注解实现呢!
- 1 首先须要自定义注解
- 2 如何获取自定义到的注解
- 3 实例化打了注解的实例
- 4 在指定bean成员变量上是否蕴含须要注入的注解,而后依赖注入
- 5 生成代理对象,基于接口判断是否抉择JDK动静代理或者CGLIB代理
代码实现
首先自定义注解
实例bean的注解 @Repository
和@Service
@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Service { String value() default "";}
@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Repository { String value() default "";}
主动拆卸的注解
@Target({ ElementType.FIELD, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Autowired { String name() default "";}
事务注解 Transactional
@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Transactional {}
而后 在类上标注注解,以及依赖注入
事务注解以及DI 以及Bean主动拆卸
我的项目构造
配置信息
版本 JDK8 , tomcat 7 , IDEA 2019 03
所需依赖
<!-- servlet --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <!--引入cglib依赖包--> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.1_2</version> </dependency>
tomcat插件
<plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <port>8080</port> <path>/</path> </configuration> </plugin>
这里咱们应用一个StartApplication
类来示意以后的顶级包下的启动类,相当于SpringBoot里的Main办法所在的类(目标仅仅是指定包,也能够在xml外面配置包名)
在Web.xml外面进行配置一下这个启动类
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" ><web-app> <display-name>tomcat启动时候启动IOC容器</display-name> <listener> <!-- 启动容器--> <!-- 注解实现--> <listener-class>com.udeam.edu.factory.impl.AnnotationBeanFactory</listener-class> <!-- xml实现ioc--> <!-- <listener-class>com.udeam.edu.factory.impl.ClassPathXmlBeanFactory</listener-class>--> </listener></web-app>
定义BeanFactory
接口类
/** * 底层BeanFactory工厂接口 * @author Pilgrim */public interface BeanFactory { /** * 存储bean单例 */ public final static Map<String, Object> IOC_MAP = new HashMap<>(); /** * 对外提供获取bean接口 * * @param id * @return bean对象 */ public Object getBean(String id); /** * 依据类型对外提供获取bean示例 * * @param classz * @return bean */ public Object getBean(Class<?> classz); /** * 获取容器中所有的bean名字 * * @return */ public Object getAllBeanName();}
抽象类AbstractBeanFactory
扩大一些属性
public abstract class AbstractBeanFactory implements BeanFactory { /** * 存储bean单例 */ public final static Map<String, Object> IOC_MAP = new HashMap<>(); /** * 容器执行一次 标识 */ public static boolean isTag = false; public static final String CLASS_STR = ".class";}
而后编写bean工厂实现类AnnotationBeanFactory
定义初始化办法
initBeanFactory(String packageName);
初始化办法蕴含以下办法
文件扫描门路
/** * 递归解决门路下文件夹是否蕴含文件夹,如不蕴含则获取以后类的权限定命名存入set中 * * @param packName * @param classNameSet * @param path */ public static void parseFilePackName(String packName, Set<String> classNameSet, String path)
bean实例化办法
private void setBean();
bean依赖注入办法
beanAutoWired()
事务处理注解办法
doScanTransactional()
/** * 注解形式 实现 Bean工厂 * * @author Pilgrim */public class AnnotationBeanFactory extends AbstractBeanFactory { /** * 2 注解 + 扫描包 形式实现 ioc 容器 * tomcat启动的时候去初始化容器 */ public AnnotationBeanFactory() { if (isTag) { return; } try { String packageName = StartApplication.class.getPackage().getName(); //扫描启动类的包名 System.out.println("------------------- [容器]正在初始化 ------------ "); System.out.println(String.format("------------------- 扫描以后包是%s ------------ ", packageName)); initBeanFactory(packageName); System.out.println("------------------- [容器]初始化实现 ------------ "); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } isTag = true; }}
包扫描
递归扫描包下的文件
com.xxx.xxx 包名须要转换成磁盘目录 c:/xxx/xx这样的模式
获取包名
String packageName = StartApplication.class.getPackage().getName();
转换包名以及扫描包失去所有的class文件名
if (Objects.isNull(packName) || packName.length() == 0) { throw new RuntimeException("有效的包门路"); } packName = packName.replace(".", File.separator); URL resource = AnnotationBeanFactory.class.getClassLoader().getResource(packName); String path = resource.getPath(); //解析中文 String filePath = URLDecoder.decode(path, "UTF-8");
递归解决
/** * 递归解决门路下文件夹是否蕴含文件夹,如不蕴含则获取以后类的权限定命名存入set中 * * @param packName * @param classNameSet * @param path */ public static void parseFilePackName(String packName, Set<String> classNameSet, String path) { File packNamePath = new File(path); if (!packNamePath.isDirectory() || !packNamePath.exists()) { return; } //递归门路下所有文件和文件夹 for (File file : packNamePath.listFiles()) { boolean directory = file.isDirectory(); String classNamePath = packName + File.separator + file.getName().replace(File.separator, "."); if (directory) { parseFilePackName(classNamePath, classNameSet, file.getPath()); } else if (file.isFile() && file.getName().endsWith(CLASS_STR)) { //存入set classNameSet.add(classNamePath.replace(File.separator, ".").replace(CLASS_STR, "")); } } }
bean实例化
失去所有的java文件名,而后去实例化bean
判断是否蕴含咱们方才自定义的注解
private void setBean() { stringSet.forEach(x -> { try { //排除指定包 servlet 类不能被实例化 这儿排除 if (!x.contains("servlet")) { Class<?> aClassz = Class.forName(x); serviceAnnotation(aClassz); repositoryAnnotation(aClassz); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } }); }
判断是否含有Service
和Repository
注解
获取bean的名字 并且判断以后类是否有 Service 注解,如有则存入Ioc 如蕴含属性value不为空,则设置value属性为bean的key
public void serviceAnnotation(Class aClass1) throws InstantiationException, IllegalAccessException { Service annotation = (Service) aClass1.getAnnotation(Service.class); if (Objects.nonNull(annotation)) { setIocNameMap(annotation.value(), aClass1.getSimpleName(), aClass1); } }
Repository 同理
public void repositoryAnnotation(Class aClass) throws InstantiationException, IllegalAccessException { Repository annotation = (Repository) aClass.getAnnotation(Repository.class); if (Objects.nonNull(annotation)) { setIocNameMap(annotation.value(), aClass.getSimpleName(), aClass); } }
获取bean的name setIocNameMap
办法而后实例化bean退出到容器
这儿判断一下是否是单例bean 这儿的单例指的是是否曾经有一个bean了
public void setIocNameMap(String value, String className, Class clasz) throws IllegalAccessException, InstantiationException { String iocNameString = value; Object beanDefinition = clasz.newInstance() ; if (value.length() > 0) { if (IOC_MAP.containsKey(value)) { throw new RuntimeException("the named" + className + ", had one ... "); } } else { //默认设置bean首字母小写的 iocNameString = getIocNameString(className); if (IOC_MAP.containsKey(iocNameString)) { throw new RuntimeException("the named " + className + ", had one ... "); } } // 依据父接口类型注入 Class<?>[] interfaces = clasz.getInterfaces(); if (interfaces != null) { for (Class<?> anInterface : interfaces) { IOC_MAP.put(anInterface.getSimpleName(), beanDefinition); } } IOC_MAP.put(iocNameString, beanDefinition); }
设置首字母小写
public static String getIocNameString(String className) { return (String.valueOf(className.toCharArray()[0])).toLowerCase() + className.substring(1, className.length()); }
依赖注入
依赖注入办法,获取成员变量上有 Autowired 注解的字段,而后依据以后类类型去主动拆卸
public static void beanAutoWired() throws ClassNotFoundException { //获取成员变量上有 Autowired 注解的字段,而后依据以后类类型去主动拆卸 for (Map.Entry<String, Object> stringObjectEntry : IOC_MAP.entrySet()) { Object beanDefinition = stringObjectEntry.getValue(); Class<?> aClass = beanDefinition.getClass(); Field[] declaredFields = aClass.getDeclaredFields(); if (Objects.isNull(declaredFields) && declaredFields.length == 0) { continue; } for (Field field : declaredFields) { //字段含有 Autowired 注解的须要被主动拆卸对象 Autowired autowired = field.getAnnotation(Autowired.class); if (Objects.nonNull(autowired)) { //依据以后key获取须要注入示例对象 //先依据名字注入,如果名字获取不到,再依据类型去注入 String beanName = autowired.name(); if (StringUtils.isEmpty(beanName)) { beanName = field.getType().getSimpleName(); } //反射设置值 try { field.setAccessible(true); //主动拆卸 线程不平安,Spring中默认单例 field.set(stringObjectEntry.getValue(), IOC_MAP.get(beanName)); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } }
扫描事务注解
public void doScanTransactional() throws IllegalAccessException, InstantiationException, ClassNotFoundException { for (Map.Entry<String, Object> classBeanDefinitionEntry : IOC_MAP.entrySet()) { Object beanDefinition = classBeanDefinitionEntry.getValue(); //判断生成代理对象 Object proxy = getProxy(beanDefinition); if (proxy==null){ proxy = beanDefinition; } //更新bean IOC_MAP.put(classBeanDefinitionEntry.getKey(), proxy); } }
判断抉择哪个代理实现形式 依据是否实现接口
public Object getProxy(Object aClass) { Object jdkProxy = null; Transactional annotation = aClass.getClass().getDeclaredAnnotation(Transactional.class); if (Objects.nonNull(annotation)) { //有接口应用jdk动静代理 if (aClass.getClass().getInterfaces() == null || aClass.getClass().getInterfaces().length <= 0) { //cglib动静代理 jdkProxy = ProxyFactory.getCglibProxy(aClass); } else { /*for (Class anInterface : aClass.getClass().getInterfaces()) { System.out.println(anInterface.getSimpleName()); }*/ jdkProxy = ProxyFactory.getJdkProxy(aClass); } } return jdkProxy; }
代理对象实现 以及办法执行前后处理事务
/** * 代理类工厂 * * @author Pilgrim */public class ProxyFactory { /** * 事务管理器 */ private final TransferServiceManager t = TransferServiceManager.get(); /** * Jdk动静代理 * * @param obj 被代理的对象 * @return 返回代理对象 */ public static Object getJdkProxy(Object obj) { Object o = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object o, Method method, Object[] objects) throws Throwable { Object invoke = null; try { // 开启事务(敞开事务的主动提交) TransferServiceManager.get().start(); invoke = method.invoke(obj, objects); // 提交事务 TransferServiceManager.get().commit(); } catch (Exception e) { e.printStackTrace(); // 回滚事务 TransferServiceManager.get().rowback(); throw e; } return invoke; } }); return o; } /** * cglib动静代理 * * @param object 被代理的对象 * @return 返回代理对象 */ public static Object getCglibProxy(Object object) { //生成代理对象 return Enhancer.create(object.getClass(), new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { Object result = null; try { //开启事务 TransferServiceManager.get().start(); result = method.invoke(object, objects); //提交事务 TransferServiceManager.get().commit(); } catch (Exception e) { //回滚事务 TransferServiceManager.get().rowback(); throw e; } return result; } }); }
初始化办法步骤
/** * 初始化bean * 1 递归扫描包获取类权限定命名 * 2 实例化bean * 3 依赖注入 * 4 扫描事务注解 生成代理对象 * * @param packName * @throws UnsupportedEncodingException */ public void initBeanFactory(String packName) throws UnsupportedEncodingException, InstantiationException, IllegalAccessException, ClassNotFoundException { if (Objects.isNull(packName) || packName.length() == 0) { throw new RuntimeException("有效的包门路"); } packName = packName.replace(".", File.separator); URL resource = AnnotationBeanFactory.class.getClassLoader().getResource(packName); String path = resource.getPath(); //解析中文 String filePath = URLDecoder.decode(path, "UTF-8"); //解析包成java权限定命名com parseFilePackName(packName, stringSet, filePath); //实例化bean setBean(); //System.out.println(String.format("获取到的bean : %s ", IOC_MAP)); //主动拆卸 beanAutoWired(); //扫描事务注解 doScanTransactional(); }
对外提供getBean(办法)
实现getBean办法
@Override //依据id名获取 public Object getBean(String id) { if (Objects.nonNull(id) && id.length() > 0) { Object beanDefinition = IOC_MAP.get(id); return beanDefinition; } return null; } @Override //依据类型获取 public Object getBean(Class<?> aClass) { if (Objects.isNull(aClass)) { return null; } return IOC_MAP.get(aClass.getSimpleName()); } @Override public Object getAllBeanName() { return IOC_MAP.keySet(); }
测试
启动tomcat能够看到启动胜利,bean实例化实现
在servlet init办法里能够调用看一下
private TransferService transferService ; @Override public void init() throws ServletException { BeanFactory beanFactory = new AnnotationBeanFactory(); transferService= (TransferService)beanFactory.getBean("transferServiceImpl"); TransferService transferService2 = (TransferService) beanFactory.getBean(TransferService.class); super.init(); }
debug能够看到 依据类型还是id都能够获取到代理之后的bean