Spring最重要的两个特点:1.依赖注入;2.切面编程,aop编程。
1.依赖注入是什么?为什么要有依赖注入?
依赖注入就是我们要使用某个对象,不是我们自己在程序里面通过new生成对象,而是通过Spring容器加载配置文件ApplicationContext.xml等生成对象;Spring容器帮助我们管理对象的创建,调用与回收;
对于依赖的对象要自己在构造函数里面生成
this.quest = new RescueDamselQuest();
这样使程序紧耦合,RescueDamselKnight只能处理RescueDamselQuest的任务,不能处理其他Quest实例 ,这样不利于组件重组,代码利用率比较低且不利于维护;
接口被传入进来,具体传进来的是什么根据Spring容器决定(Quest可以有很多实现);使用的是哪个具体的Quest,BravaKnight并不知道,只有配置文件才晓得。Spring项目里面也是通过注解@Autowired Interface,生成实现该接口的一个实例,而具体由谁实习该接口,配置文件里面会定义。
2. 为什么需要AOP编程?
aop,面向切面编程,这个是对oop(面向对象编程)的补充;oop是用来完成核心业务逻辑,但是有一些公共性的行为,比如日志,权限验证等可能在很多对象里面里面都有用到,我们不能在这些对象的程序里面把日志和权限验证的代码都写一遍,这样太繁琐了而且对象之间的耦合性很高。所以我们把这些公共性的行为抽象封装起来,在配置文件里面决定在哪些地方使用这些行为。
2.1动态代理实现aop编程
接口IHello
public interface IHello{ public void sayHi(String str); }
实现IHello的类
public class Hello implements IHello { public void sayHi(String str){ System.out.println(str); } }
我们想在方法sayHi()的前后添加一些操作,一个方法就是新建一个对象HelloCon。
public class HelloCon implements IHello{ private IHello hello; public HelloCon(IHello hello){ this.hello = hello; } public void sayHi(String str){ //操作1 hello.sayHi(str); //操作2; } }
另一个方法是我们使用代理模式访问Hello对象,与该代理对象绑定有一个InvocationHandler接口,我们重写该接口,操作1,操作2放在invoke()方法里面完成; 实现代理主要分为两部分:1.生成代理对象;2.重写InvocationHandler的invoke()方法。
public class HelloHander implements InvocationHandler{ public IHello hello; public HelloHander(IHello hello){ this.hello = hello; } public Object getProxyObject(){ return Proxy.newProxyInstance( this.getClass().getClassLoader(),IHello.class,this); } //proxy:与该Handler绑定的代理对象; //method:使用反射来实现代理对象对真实对象方法的调用 //args:方法的形参 public Object invoke(Object proxy, Method method, Object[] args) { //操作1 method.invoke(hello,args); //操作2 return null; } }
/* loader:用来定义代理类的类装载器 interfaces:代理类实现的接口 h:实际处理的内容放在h里面 */ public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException{ //将代理类将要实现的接口的字节码复制到intfs中 final Class<?>[] intfs = interfaces.clone(); //如果这个代理类已经存在(该代理类由loader定义,并且实现了给定的接口),直接复制缓存返回即可,如果不存在,通过ProxyClassFactory创建新的代理类 Class<?> cl = getProxyClass0(loader, intfs); // 得到构造函数;constructorParams的构造函数是 //constructorParams(InvocationHandler.class) final Constructor<?> cons = cl.getConstructor(constructorParams); //构造函数生成新的代理对象,传入的参数是代理对象要关联的invocationhandler return cons.newInstance(new Object[]{h}); }