进入第二天了,今天要学的东西就比较深入一些了。
第一部分 依赖注入
在昨天我们已经实现了bean的创建和管理。既然创建了bean,那就需要使用,也就是当一个bean要使用到别的bean,就得把这个bean给他放入需要bean的类中吧。
怎么实现呢?首先肯定想到怎么标识需要注入的地方,用过Spring的都知道有一个@Autowired注解。那我们就把他创建出来。
1 2 3 4 5
| @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Autowired { String value() default ""; }
|
我们再创建一个新的OrderService,把他也注册成bean。然后,在原先的UserService中去注入这个OrderService的bean。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Component public class OrderService { }
@Component public class UserService {
@Autowired private OrderService orderService;
public void test(){ System.out.println(orderService); } }
|
好了,现在标识已经做好了。那不能只有表面功夫吧,加个注解就完事;肯定要为他去实现一个依赖注入的功能。要在哪里实现呢?
想想昨天写过的东西,构造函数?似乎只是用来扫描和把创建出来的bean加入单例池;getBean?更不对了,只是用来获取bean的,都不知道要注入到哪里……
对了!createBean可太适合不过了!在昨天的代码里,这里面可是把每一个bean进行实例化的地方,那实例化之后,跟着把里面的内容一起给他放进去,不是妙哉?
所以,我们先获取实例化的实例所有内部字段,再一个个遍历判断是否有加上注解,如果加上了@Autowired,那就进行注入就好了。
那怎么注入呢?首先需要拿到注入的bean实例,getBean方法就有用了,把字段的名字传给getBean,这样就拿到了需要注入的一个实例,我们写好的getBean会帮我们解决单多例的问题。
然后就可以通过反射把这个bean注入了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| private Object createBean(String beanName, BeanDefinition beanDefinition) { Class clazz = beanDefinition.getType();
try { Object instance = clazz.getConstructor().newInstance(); for (Field field : clazz.getDeclaredFields()) { if(field.isAnnotationPresent(Autowired.class)){ field.setAccessible(true); field.set(instance,getBean(field.getName())); } } return instance; } catch (InstantiationException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { throw new RuntimeException(e); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } }
|
这样我们就实现bean的DI了!用测试方法看看bean的打印情况:
1 2 3 4 5 6 7 8 9
| public class Test { public static void main(String[] args) {
SwissApplicationContext swissApplicationContext = new SwissApplicationContext(AppConfig.class);
UserService userService = (UserService) swissApplicationContext.getBean("userService"); userService.test(); } }
|
1
| com.swiss.service.OrderService@6f94fa3e
|
依赖注入就实现了。
第二部分 Aware回调
在有的时候,我们在bean实例化和依赖注入后,想要得到一些注册出的bean内容,比如我并不知道bean名字在Spring容器中的默认命名规则,但我想要知道当前这个bean在IOC中以什么名字存储,这个时候就需要一个回调,我们可以根据这个回调,拿到bean的一些有关信息,这就是Aware回调。
这里我们模拟一个bean名字获取的回调,创建回调接口:
1 2 3 4
| public interface BeanNameAware {
void setBeanName(String beanName); }
|
然后我们就可以去实现这个接口,得到beanName。注意,这个回调函数是在Spring中自动被调用的,也就是Spring会自动把bean名字传进来。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Component public class UserService implements BeanNameAware {
@Autowired private OrderService orderService;
private String beanName;
@Override public void setBeanName(String beanName) { this.beanName = beanName; }
public void test(){ System.out.println(orderService); } }
|
这样我们通过beanName的Aware回调,拿到了想要的bean的名字。
当然实际上还有好多回调:BeanClassLoaderAware,BeanFactoryAware,ApplicationContextAware……但是都和这个原理相同。
第三部分 初始化回调
在bean处理好了之后,我们想要对其进行一些预先的操作,比如给一些东西赋值啊之类的,这个时候就是属于初始化的内容了。
Spring为初始化回调提供了一个接口,和上面的Aware一样,我们需要自己去实现,然后进行自己想要的操作。注意,这里面你想做什么,Spring都不会管,也不会传来任何参数,完全是你自己的逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public interface InitializingBean { void afterPropertiesSet(); }
@Component public class UserService implements BeanNameAware, InitializingBean {
@Autowired private OrderService orderService;
private String beanName;
private String INeed;
@Override public void setBeanName(String beanName) { this.beanName = beanName; }
@Override public void afterPropertiesSet() { this.INeed = "Mika"; }
public void test(){ System.out.println(orderService); } }
|
比如在这里,我为我的一个成员变量赋了一个值,我还可以输出,存储……干什么都可以。这就是初始化的回调作用。
第四部分 BeanPostProcessor Bean的后置处理器
为了更好、更灵活的管理bean对象,Spring还在初始化的前后提供了处理的方法。BeanPostProcessor接口就是用来让插入在初始化前后的一些想要执行的逻辑。
1 2 3 4 5 6
| public interface BeanPostProcessor {
void postProcessBeforeInitialization(String beanName, Object bean);
void postProcessAfterInitialization(String beanName, Object bean); }
|
这两个方法分别在初始化前、后调用。
我们可以试着实现以下这两个方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Component public class SwissBeanPostProcessor implements BeanPostProcessor { @Override public void postProcessBeforeInitialization(String beanName, Object bean) { if(beanName.equals("userService")){ System.out.println("1111"); } }
@Override public void postProcessAfterInitialization(String beanName, Object bean) { if(beanName.equals("userService")){ System.out.println("2222"); } } }
|
我们实现的逻辑是:bean的名字是userService的时候,在初始化前打印一个1111,在初始化后打印一个2222。
这些调用在我们写好逻辑后,Spring会帮我们自动在初始化前后调用。那Spring是怎么知道有哪些后置处理器的?
这个需要在Spring里面进行处理。在注册完所有BeanDefinition后,专门处理BeanPostProcessor的创建,使其被初始化成一个成熟的bean,然后加入List进行管理。
在处理其他正常bean的时候,遇到BeanPostProcessor子类就跳过即可。
但是!这里为了简单方便,我们在构造函数里面扫描的时候就直接创建BeanPostProcessor实例!实际上不应该是这样,会导致这样创建出来的实例没有被DI,是个残缺的bean,所以下面这一段代码仅供参考。
1 2 3 4 5 6 7 8 9
| if (clazz.isAnnotationPresent(Component.class)) {
if (BeanPostProcessor.class.isAssignableFrom(clazz)) { BeanPostProcessor instance = (BeanPostProcessor) clazz.newInstance(); beanPostProcessorList.add(instance); } ……
|
然后,在初始化前后,遍历bean后置处理器的List,执行定义的方法即可。
1 2 3 4 5 6 7 8 9 10 11 12
| for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) { instance = beanPostProcessor.postProcessBeforeInitialization(beanName,instance); }
if (instance instanceof InitializingBean) { ((InitializingBean) instance).afterPropertiesSet(); }
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) { instance = beanPostProcessor.postProcessAfterInitialization(beanName,instance); }
|
我们试着运行一下:
1 2 3
| 1111 Mika(afterPropertiesSet,初始化方法打印) 2222
|
确实是在初始化的前后插入了逻辑。
这就是bean的后置处理器BeanPostProcessor。在Spring里面实现了很多BeanPostProcessor,如下面的AOP。当然我们也可以自己实现BeanPostProcessor,执行想要的逻辑。
第五部分 AOP
在上一个部分中,讲到了Bean的后置处理器,可以在Bean被初始化的前后执行一些逻辑。
但我们知道,我们有时候可能对于某个bean需要增强,如AOP的切面。这个时候,我们就需要代理对象来实现增强的内容,而不是原生的bean实例。
在Spring中,有一个AnnotationAwareAspectJAutoProxyCreator,会解析@Aspect里面的内容,根据匹配的切点表达式得到是哪个bean,然后进行动态代理创建,构造一个拦截器链,按顺序执行前置通知、目标方法、后置通知等等。
但我们这里同样是一个模拟,简单的处理。
首先我们需要把上一部分的postProcessBeforeInitialization和postProcessAfterInitialization返回值改为Object。因为需要返回一个代理对象了。
然后在postProcessAfterInitialization里面,也就是初始化完成后,我们将进行动态代理的创建。这里为userService增强,创建一个JDK动态代理,首先需要创建一个interface(JDK只支持接口代理):
1 2 3
| public interface UserInterface { void test(); }
|
然后我们让UserService去实现这个接口,就可以为UserService创建动态代理了。
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Override public Object postProcessAfterInitialization(String beanName, Object bean) { if (beanName.equals("userService")) { return Proxy.newProxyInstance(SwissApplicationContext.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("切面逻辑"); return method.invoke(bean, args); } }); } return bean; }
|
可以看到,这里userService的bean已经将变成代理对象返回。那我们尝试使用getBean得到的bean去进行原先的test方法:
1 2
| 切面逻辑 com.swiss.service.OrderService@355da254
|
成功了!增强逻辑被实现了,也就是我们的AOP动态代理增强实现了。
第二天的学习内容结束了!