@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)
@Documented
public @interface Service {String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Repository {String value() default "";
}
主动拆卸的注解
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {String name() default "";
}
事务注解 Transactional
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @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