@TOC
前言
上一篇點(diǎn)擊查看使用xml來(lái)實(shí)現(xiàn)自定義IOC以及依賴(lài)注入關(guān)系維護(hù),動(dòng)態(tài)代理,以及事務(wù)操作;
這次使用注解來(lái)實(shí)現(xiàn)IOC以及依賴(lài)關(guān)系維護(hù)
步驟以及思路分析
基于xml實(shí)現(xiàn)方式時(shí),僅僅只需要在xml里面配置好bean的id以及bean的權(quán)限定命名,然后反射實(shí)例化對(duì)象,最后加入到ioc容器中
依賴(lài)注入時(shí)候僅僅需要獲取property
標(biāo)簽以及父級(jí)標(biāo)簽,根據(jù)property
名從ioc容器中獲取到需要注入的bean示例即可;
如果是基于注解實(shí)現(xiàn)呢!
- 1 首先需要自定義注解
- 2 如何獲取自定義到的注解
- 3 實(shí)例化打了注解的實(shí)例
- 4 在指定bean成員變量上是否包含需要注入的注解,然后依賴(lài)注入
- 5 生成代理對(duì)象,基于接口判斷是否選擇JDK動(dòng)態(tài)代理或者CGLIB代理
代碼實(shí)現(xiàn)
首先自定義注解
實(shí)例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 "";
}
自動(dòng)裝配的注解
@Target({ ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
String name() default "";
}
事務(wù)注解 Transactional
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Transactional {
}
然后 在類(lèi)上標(biāo)注注解,以及依賴(lài)注入
事務(wù)注解以及DI 以及Bean自動(dòng)裝配
項(xiàng)目結(jié)構(gòu)
配置信息
版本 JDK8 , tomcat 7 , IDEA 2019 03
所需依賴(lài)
<!-- servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!--引入cglib依賴(lài)包-->
<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>
這里我們使用一個(gè)StartApplication
類(lèi)來(lái)表示當(dāng)前的頂級(jí)包下的啟動(dòng)類(lèi),相當(dāng)于SpringBoot里的Main方法所在的類(lèi)(目的僅僅是指定包,也可以在xml里面配置包名)
在Web.xml里面進(jìn)行配置一下這個(gè)啟動(dòng)類(lèi)
<!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啟動(dòng)時(shí)候啟動(dòng)IOC容器</display-name>
<listener>
<!-- 啟動(dòng)容器-->
<!-- 注解實(shí)現(xiàn)-->
<listener-class>com.udeam.edu.factory.impl.AnnotationBeanFactory</listener-class>
<!-- xml實(shí)現(xiàn)ioc-->
<!-- <listener-class>com.udeam.edu.factory.impl.ClassPathXmlBeanFactory</listener-class>-->
</listener>
</web-app>
定義BeanFactory
接口類(lèi)
/**
* 底層BeanFactory工廠接口
* @author Pilgrim
*/
public interface BeanFactory {
/**
* 存儲(chǔ)bean單例
*/
public final static Map<String, Object> IOC_MAP = new HashMap<>();
/**
* 對(duì)外提供獲取bean接口
*
* @param id
* @return bean對(duì)象
*/
public Object getBean(String id);
/**
* 根據(jù)類(lèi)型對(duì)外提供獲取bean示例
*
* @param classz
* @return bean
*/
public Object getBean(Class<?> classz);
/**
* 獲取容器中所有的bean名字
*
* @return
*/
public Object getAllBeanName();
}
抽象類(lèi)AbstractBeanFactory
擴(kuò)展一些屬性
public abstract class AbstractBeanFactory implements BeanFactory {
/**
* 存儲(chǔ)bean單例
*/
public final static Map<String, Object> IOC_MAP = new HashMap<>();
/**
* 容器執(zhí)行一次 標(biāo)識(shí)
*/
public static boolean isTag = false;
public static final String CLASS_STR = ".class";
}
然后編寫(xiě)bean工廠實(shí)現(xiàn)類(lèi)AnnotationBeanFactory
定義初始化方法
initBeanFactory(String packageName);
初始化方法包含以下方法
文件掃描路徑
/**
* 遞歸處理路徑下文件夾是否包含文件夾,如不包含則獲取當(dāng)前類(lèi)的權(quán)限定命名存入set中
*
* @param packName
* @param classNameSet
* @param path
*/
public static void parseFilePackName(String packName, Set<String> classNameSet, String path)
bean實(shí)例化方法
private void setBean();
bean依賴(lài)注入方法
beanAutoWired()
事務(wù)處理注解方法
doScanTransactional()
/**
* 注解方式 實(shí)現(xiàn) Bean工廠
*
* @author Pilgrim
*/
public class AnnotationBeanFactory extends AbstractBeanFactory {
/**
* 2 注解 + 掃描包 方式實(shí)現(xiàn) ioc 容器
* tomcat啟動(dòng)的時(shí)候去初始化容器
*/
public AnnotationBeanFactory() {
if (isTag) {
return;
}
try {
String packageName = StartApplication.class.getPackage().getName();
//掃描啟動(dòng)類(lèi)的包名
System.out.println("------------------- [容器]正在初始化 ------------ ");
System.out.println(String.format("------------------- 掃描當(dāng)前包是%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 包名需要轉(zhuǎn)換成磁盤(pán)目錄 c:/xxx/xx這樣的形式
獲取包名
String packageName = StartApplication.class.getPackage().getName();
轉(zhuǎn)換包名以及掃描包得到所有的class文件名
if (Objects.isNull(packName) || packName.length() == 0) {
throw new RuntimeException("無(wú)效的包路徑");
}
packName = packName.replace(".", File.separator);
URL resource = AnnotationBeanFactory.class.getClassLoader().getResource(packName);
String path = resource.getPath();
//解析中文
String filePath = URLDecoder.decode(path, "UTF-8");
遞歸處理
/**
* 遞歸處理路徑下文件夾是否包含文件夾,如不包含則獲取當(dāng)前類(lèi)的權(quán)限定命名存入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實(shí)例化
得到所有的java文件名,然后去實(shí)例化bean
判斷是否包含我們剛才自定義的注解
private void setBean() {
stringSet.forEach(x -> {
try {
//排除指定包 servlet 類(lèi)不能被實(shí)例化 這兒排除
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的名字 并且判斷當(dāng)前類(lèi)是否有 Service 注解,如有則存入Ioc 如包含屬性value不為空,則設(shè)置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
方法然后實(shí)例化bean加入到容器
這兒判斷一下是否是單例bean 這兒的單例指的是是否已經(jīng)有一個(gè)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 {
//默認(rèn)設(shè)置bean首字母小寫(xiě)的
iocNameString = getIocNameString(className);
if (IOC_MAP.containsKey(iocNameString)) {
throw new RuntimeException("the named " + className + ", had one ... ");
}
}
// 根據(jù)父接口類(lèi)型注入
Class<?>[] interfaces = clasz.getInterfaces();
if (interfaces != null) {
for (Class<?> anInterface : interfaces) {
IOC_MAP.put(anInterface.getSimpleName(), beanDefinition);
}
}
IOC_MAP.put(iocNameString, beanDefinition);
}
設(shè)置首字母小寫(xiě)
public static String getIocNameString(String className) {
return (String.valueOf(className.toCharArray()[0])).toLowerCase() + className.substring(1, className.length());
}
依賴(lài)注入
依賴(lài)注入方法,獲取成員變量上有 Autowired 注解的字段,然后根據(jù)當(dāng)前類(lèi)類(lèi)型去自動(dòng)裝配
public static void beanAutoWired() throws ClassNotFoundException {
//獲取成員變量上有 Autowired 注解的字段,然后根據(jù)當(dāng)前類(lèi)類(lèi)型去自動(dòng)裝配
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 注解的需要被自動(dòng)裝配對(duì)象
Autowired autowired = field.getAnnotation(Autowired.class);
if (Objects.nonNull(autowired)) {
//根據(jù)當(dāng)前key獲取需要注入示例對(duì)象
//先根據(jù)名字注入,如果名字獲取不到,再根據(jù)類(lèi)型去注入
String beanName = autowired.name();
if (StringUtils.isEmpty(beanName)) {
beanName = field.getType().getSimpleName();
}
//反射設(shè)置值
try {
field.setAccessible(true);
//自動(dòng)裝配 線程不安全,Spring中默認(rèn)單例
field.set(stringObjectEntry.getValue(), IOC_MAP.get(beanName));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
掃描事務(wù)注解
public void doScanTransactional() throws IllegalAccessException, InstantiationException, ClassNotFoundException {
for (Map.Entry<String, Object> classBeanDefinitionEntry : IOC_MAP.entrySet()) {
Object beanDefinition = classBeanDefinitionEntry.getValue();
//判斷生成代理對(duì)象
Object proxy = getProxy(beanDefinition);
if (proxy==null){
proxy = beanDefinition;
}
//更新bean
IOC_MAP.put(classBeanDefinitionEntry.getKey(), proxy);
}
}
判斷選擇哪個(gè)代理實(shí)現(xiàn)方式 根據(jù)是否實(shí)現(xiàn)接口
public Object getProxy(Object aClass) {
Object jdkProxy = null;
Transactional annotation = aClass.getClass().getDeclaredAnnotation(Transactional.class);
if (Objects.nonNull(annotation)) {
//有接口使用jdk動(dòng)態(tài)代理
if (aClass.getClass().getInterfaces() == null || aClass.getClass().getInterfaces().length <= 0) {
//cglib動(dòng)態(tài)代理
jdkProxy = ProxyFactory.getCglibProxy(aClass);
} else {
/*for (Class anInterface : aClass.getClass().getInterfaces()) {
System.out.println(anInterface.getSimpleName());
}*/
jdkProxy = ProxyFactory.getJdkProxy(aClass);
}
}
return jdkProxy;
}
代理對(duì)象實(shí)現(xiàn) 以及方法執(zhí)行前后處理事務(wù)
/**
* 代理類(lèi)工廠
*
* @author Pilgrim
*/
public class ProxyFactory {
/**
* 事務(wù)管理器
*/
private final TransferServiceManager t = TransferServiceManager.get();
/**
* Jdk動(dòng)態(tài)代理
*
* @param obj 被代理的對(duì)象
* @return 返回代理對(duì)象
*/
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 {
// 開(kāi)啟事務(wù)(關(guān)閉事務(wù)的自動(dòng)提交)
TransferServiceManager.get().start();
invoke = method.invoke(obj, objects);
// 提交事務(wù)
TransferServiceManager.get().commit();
} catch (Exception e) {
e.printStackTrace();
// 回滾事務(wù)
TransferServiceManager.get().rowback();
throw e;
}
return invoke;
}
});
return o;
}
/**
* cglib動(dòng)態(tài)代理
*
* @param object 被代理的對(duì)象
* @return 返回代理對(duì)象
*/
public static Object getCglibProxy(Object object) {
//生成代理對(duì)象
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 {
//開(kāi)啟事務(wù)
TransferServiceManager.get().start();
result = method.invoke(object, objects);
//提交事務(wù)
TransferServiceManager.get().commit();
} catch (Exception e) {
//回滾事務(wù)
TransferServiceManager.get().rowback();
throw e;
}
return result;
}
});
}
初始化方法步驟
/**
* 初始化bean
* 1 遞歸掃描包獲取類(lèi)權(quán)限定命名
* 2 實(shí)例化bean
* 3 依賴(lài)注入
* 4 掃描事務(wù)注解 生成代理對(duì)象
*
* @param packName
* @throws UnsupportedEncodingException
*/
public void initBeanFactory(String packName) throws UnsupportedEncodingException, InstantiationException, IllegalAccessException, ClassNotFoundException {
if (Objects.isNull(packName) || packName.length() == 0) {
throw new RuntimeException("無(wú)效的包路徑");
}
packName = packName.replace(".", File.separator);
URL resource = AnnotationBeanFactory.class.getClassLoader().getResource(packName);
String path = resource.getPath();
//解析中文
String filePath = URLDecoder.decode(path, "UTF-8");
//解析包成java權(quán)限定命名com
parseFilePackName(packName, stringSet, filePath);
//實(shí)例化bean
setBean();
//System.out.println(String.format("獲取到的bean : %s ", IOC_MAP));
//自動(dòng)裝配
beanAutoWired();
//掃描事務(wù)注解
doScanTransactional();
}
對(duì)外提供getBean(方法)
實(shí)現(xiàn)getBean方法
@Override
//根據(jù)id名獲取
public Object getBean(String id) {
if (Objects.nonNull(id) && id.length() > 0) {
Object beanDefinition = IOC_MAP.get(id);
return beanDefinition;
}
return null;
}
@Override
//根據(jù)類(lèi)型獲取
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();
}
測(cè)試
啟動(dòng)tomcat可以看到啟動(dòng)成功,bean實(shí)例化完成
在servlet init方法里可以調(diào)用看一下
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可以看到 根據(jù)類(lèi)型還是id都可以獲取到代理之后的bean