@TOCjava
上一篇點擊查看使用xml來實現自定義IOC以及依賴注入關係維護,動態代理,以及事務操做;web
此次使用註解來實現IOC以及依賴關係維護apache
基於xml實現方式時,僅僅只須要在xml裏面配置好bean的id以及bean的權限定命名,而後反射實例化對象,最後加入到ioc容器中
依賴注入時候僅僅須要獲取property
標籤以及父級標籤,根據property
名從ioc容器中獲取到須要注入的bean示例便可;api
若是是基於註解實現呢!tomcat
實例bean的註解 @Repository
和@Service
安全
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Service { String value() default ""; }
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Repository { String value() default ""; }
自動裝配的註解app
@Target({ ElementType.FIELD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Autowired { String name() default ""; }
事務註解 Transactionalmaven
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Transactional { }
而後 在類上標註註解,以及依賴注入
事務註解以及DI 以及Bean自動裝配ide
版本 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)
private void setBean();
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, "")); } } }
獲得全部的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方法
@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