之前已经完成了代理的获取,在此基础上完成注入,并添加半自动化添加拦截器,简化用户使用。
首先,回顾一下之前获取代理的过程。用户给出包名称,通过扫描包,将加相应注解的类,方法,进行处理,获取代理(方法是为方法的返回值类型获取代理)。将全部代理存起来放在map中,待用。
接着来实现我们今天的注入。
一、注入
用户通过类名字,获取代理。而我们第一步要判断这个类是否被处理过,看它有没有加相应的注解,如果没有加,那就不存在下一步了。第二步取出代理,注入。给这个类中的全部成员注入值,值就是该类的ProxyClass类中的object成员。然后就可以将处理好的代理给用户了。用户得到代理,直接使用即可。
问题一(ProxyClass类中的object和proxy):
运行一个带参方法的时候肯定是给它所需要的值,然后运行。ProxyClass类中的object成员是有值的,这个值是用户给的,我们当时把值存下来做object成员了。所以现在运行的时候要用ProxyClass类中的object成员,拦截时才使用代理,不要弄混。
问题二(单例模式):
这里的单例不同于我们平常的单例模式。本次代码中,默认每一个类仅有一个代理,即只有一个ProxyClass类,是单例的。若有多个则需要另行处理。
问题三(方法的运行):
产生代理的时候,我们写了invoke,但事实上,这一段产生代理的时候并不运行,到用户使用代理运行方法的时候,这一段才运行,invoke并拦截。
//BeanFactory类中
/**注入
* @param <proxyBeanFactory> 工具
* @param <object> 需要注入的类的对象
* */
private static void injection(ProxyBeanFactory proxyBeanFactory,
Object object) {
//得到全部字段 (成员)
Field[] fields = object.getClass().getDeclaredFields();
for(Field f : fields) {
if(!f.isAnnotationPresent(Autowired.class)) {
return;
}
//获取该类的ProxyClass 类
ProxyClass klassProxyClass = proxyBeanFactory.getProxyClass(object.getClass().getName());
//设置注入成员为true
klassProxyClass.setInject(true);
//判断成员的类是否注入,没有继续处理
ProxyClass fieldProxyClass = proxyBeanFactory.getProxyClass(f.getType().getName());
if(!fieldProxyClass.isInject()) {
injection(proxyBeanFactory,
fieldProxyClass.getObject());
}
f.setAccessible(true);
try {
//将值注入
f.set(object, fieldProxyClass.getProxy());
} catch (Exception e) {
e.printStackTrace();
}
}
}
@SuppressWarnings("unchecked")
public static <T> T getBean(Class<?> klass) {
//判断该类是否有注解
if(!klass.isAnnotationPresent(Component.class)){
System.out.println("该类没有注解");
return null;
}
//获取代理
ProxyBeanFactory proxyBeanFactory = new ProxyBeanFactory();
ProxyClass proxyClass = proxyBeanFactory.getProxyClass(klass.getName());
//是否注入,注入成员
if(!proxyClass.isInject()) {
injection(proxyBeanFactory,proxyClass.getObject());
}
//将注入好的代理返回
return (T) proxyClass.getProxy();
}
问题四(循环依赖):
循环依赖问题是指A类有B类的成员,B类有C类的成员,C类有A类的成员,形成一个环,注入是A等B注入,B等C注入,C等A注入,陷入死循环,此处为了解决这个问题,我们给 proxyClass类中加了一个Inject成员,未注入设为false,注入设为true,每次在未注入前就setInject为true,解决了这个问题。
主函数:
public static void main(String[] args) {
BeanFactory.scanPackage("com.zty.student");
ScanIntercepter.scanIntercepterMethods("com.mec.student");
StudentAction action = BeanFactory.getBean(StudentAction.class);
action.getStudentById("12345678");
}
二、添加拦截器
类似于之前的扫描,用户给包名,扫描,处理带相应注解的,将拦截器存入map中,按一个方法对应一个拦截器链,做成键值对,存入map,即map<方法,拦截器链List>,放入intercepterFactory,而不是将其存入ProxyClass,使用时根据类和方法从intercepterFactory中取出相应的拦截器链,使用即可。
/**扫描指定包,里面加拦截器的方法已经写好注解,
* 根据不同注解,处理不同的拦截器方法,并把其加入不同的map中
* */
public static void scanIntercepterMethods(String packageName) {
new PackageScanner() {
@Override
//klass,是一个里面有拦截器的类
public void dealClass(Class<?> klass) {
if(!klass.isAnnotationPresent(Aspect.class)) {
return;
}
try {
Object object = klass.newInstance();
Method[] methods = klass.getDeclaredMethods();
for(Method method : methods) {
if(method.isAnnotationPresent(Before.class)) {
dealBeforeIntercepter(klass, object, method);
} else if(method.isAnnotationPresent(After.class)) {
dealAfterIntercepter(klass, object, method);
} else if (method.isAnnotationPresent(ThrowException.class)) {
dealExceptionIntercepter(klass, object, method);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.packageScan(packageName);
}
/**处理前置拦截器方法
* @param <Class> 拦截器所在的类
* @param <Object> 拦截器所在的类的对象
* @param <Method> 拦截器方法
* @throws SecurityException
* @throws NoSuchMethodException
* */
private static void dealBeforeIntercepter(Class<?> klass, Object object,
Method method) throws Exception {
//返回值类型不对
if(!method.getReturnType().equals(boolean.class)) {
throw new ReturnTypeWrongException(method.getName() + "返回值类型错误");
}
//要拦截的方法
Before before = method.getAnnotation(Before.class);
Class<?> targetClass = before.klass();
Method targetMethod = targetClass.getMethod(before.methodName(),
method.getParameterTypes());
IntercepterMethodDefination imd = new IntercepterMethodDefination(klass, method, object);
IntercepterTargetDefination itd = new IntercepterTargetDefination(targetClass, targetMethod);
//存入factory
IntercepterFactory intercepterFactory = new IntercepterFactory();
intercepterFactory.addBeforeIntercepter(itd, imd);
}
处理三种拦截器的方法类似,不再给出代码。
//存储拦截器的类
//其中有三个map
private static final Map<IntercepterTargetDefination,
List<IntercepterMethodDefination>> beforeInterceptsMap;
private static final Map<IntercepterTargetDefination,
List<IntercepterMethodDefination>> afterInterceptsMap;
private static final Map<IntercepterTargetDefination,
List<IntercepterMethodDefination>> exceptionInterceptsMap;
//添加拦截器
private void addIntercepter(Map<IntercepterTargetDefination,
List<IntercepterMethodDefination>> map,
IntercepterTargetDefination itd,
IntercepterMethodDefination imd) {
List<IntercepterMethodDefination> imdList = map.get(itd);
System.out.println(itd.getMethod() + "***"
+ imdList);
//这个list只能有一份,必须是单例的,每个方法只能有一个list
if (imdList == null) {
synchronized(IntercepterFactory.class) {
if(imdList == null) {
imdList = new ArrayList<>();
map.put(itd, imdList);
}
}
}
imdList.add(imd);
}
//添加前置拦截器
public void addBeforeIntercepter(IntercepterTargetDefination itd,
IntercepterMethodDefination imd) {
addIntercepter(beforeInterceptsMap, itd, imd);
}