1、控制反转(IOC)
将创建管理对象的工作交给容器来做。在容器初始化(或在某个时间节点)通过反射机制创建好对象,在使用时直接从容器中获取。
- 控制反转:将对象的控制权反过来交给容器管理。
- 依赖注入(DI):将对象依赖写入配置文件,在创建有依赖关系的对象时,由IOC容器来注入依赖的兑现。构造器注入、setter方法注入。
1.1、实现原理(流程):(反射+XML技术)
利用(反射+工厂)技术,根据配置文件中给出的类名生成相应的对象。
Class.forName(className).newInstance。
- Tomcat启动Spring容器;
- 初始化Spring容器,加载application.xml配置文件;
- 获取扫描包下所有class文件;
- 解析class中的注解信息;
- 通过反射实例化相应bean对象,以<beanId,bean>的形式保存集合,存储在IOC容器中。
- 通过ApplicationContext的getBean方法获取Bean。
1.2、IOC的好处
创建Bean及其依赖对象的工作交给IOC容器管理,业务代码只需要getBean就行了。将依赖关系写入配置文件中,有修改时,直接修改配置文件即可,而不用去业务代码中每一个使用Bean的地方修改。
1.3、IOC类型
- 构造注入:通过容器触发一个类的构造器来实现的,该类有一系列参数,每个参数代表一个对其他类的依赖。(对象的依赖关系可在构造函数中完成)。
- Setter方法注入:容器通过调用无参构造器或无参static方法实例化bean之后,调用bean的setter方法。
1.4、IOC的初始化过程
资源定位、资源载入BeanDefinition、<BeanName、BeanDefiniton>注册到IOC容器。
a、Resource定位(Bean的定义文件定位),返回Resource对象
** Resource对象与对应ApplicationContext类型如下**:
**** FileSystemResource(绝对路径定位********):FileSystemXmlApplicationContext;
**** ClassPathResourcee(类路径定位):ClassPathXmlApplicationContext;
**** ServletContextResource(web应用根目录********):XmlWebApplicationContext;
**** UrlResource:访问网络资源的实现类。例如file: http: ftp:等前缀的资源对象;
**** ByteArrayResource: ****访问字节数组资源的实现类。
@Configuation:AnnotationConfigApplicationContext
Spring提供ResourceLoader接口用于实现不同的Resource加载策略,所有的ApplicationContext均实现了ResourceLoader。
b、将Resource定位好的资源载入到BeanDefinition
加载Resource对象中的Bean配置成BeanDefinition,如果Bean有依赖关系,则使用占位符(getBean时,将占位符替换成相应的Bean)暂时代替。
c、将<BeanName, BeanDefinition>注册到IOC容器中
1.5、拓展:反射和new
- new只能用于编译期就能确定的类型, 而反射可以在运行时才确定类型并创建其对象。例如:Spring是事先写好的框架,在编译时其内部处理并不知道将来用户要加载哪些类到IOC容器中,此时就需要使用到反射;
- 使用new出来的对象无法访问私有属性,使用反射出来的对象可以通过setAccessible访问私有属性。
- 使用反射可以使程序代码更加灵活,使用运行期的动态加载。
2、AOP(面向切面编程)
将一些系统性相关(日志、事务、安全)的编程工作,独立提取出来,独立实现,作为一个**公共切面**切入到相关的业务逻辑中。
将业务逻辑代码与公共功能代码分离开,使开发人员能更专注地编写业务逻辑代码。
在不改变业务逻辑代码的前提下,在业务逻辑之前、之后、或者周围添加横切关注点(切面),对目标功能进行扩展。
2.1、AOP的应用场景
事务(数据库更新),权限(web请求权限),日志(每一次更新记录)等。
2.2、AOP基本术语切点、切面、连接点、通知等
- 通知:定义AOP切面执行的工作,以及切面执行的时间。(Before、After、After-returning、After-throwing、Around)
- 切点:定义切面执行的地点,满足配置条件的目标方法(在什么地方执行切面)。
- 连接点:切点的全集。一个程序可能会有多个切面,不同切面映射需要不同切点。
- 切面:切点+通知。在什么时候、什么地点、执行什么工作。
2.3、AOP的两种实现方式(注解、xml文件)
2.3.1、XML方式
普通工程通过new ClassPathXmlApplicationContext("applicationContext.xml")初始化Spring。
在执行DogService类中的方法时,执行前会先执行PointCutXmlConf类中的beforeAdvice方法,执行后会执行PointCutXmlConf类中的afterAdvice方法。
2.3.2、注解方式
2.4、AOP原理(动态代理)
2.4.1、JDK(动态)代理(反射)
**** 利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
动态代理实际上是程序在运行中,根据被代理的接口来动态生成代理类的class文件,并加载class文件运行的过程。
而动态生成的代理类已经继承了Proxy类,由于Java的单继承,所以只能靠实现被代理类的接口的形式,而不是靠继承被代理类的形式。
JVM编译完成时并没有实际的字节码文件,而是在运行时动态生成类字节码,并加载到JVM中。
优点:通过反射,可以有效减少代理类的数量,使用更灵活;
缺点:反射代理,比较消耗系统性能。
目标对象实现接口,代理对象可以不实现业务接口。
主要类:
Java.lang.reflect.Proxy以及Java.lang.reflect. InvocationHandler接口(事件处理器)
Proxy类:创建一个代理对象
InvocationHandler接口:代理实例的调用处理程序。
主要方法:
JDK代理类实例:
public class JingJiPenple{
//维护一个目标对象
private Object target;
public JingJiPenple(Object target){
this.target=target;
}
//给目标对象生成代理对象
public Object getProxyInstance(){
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("动态代理---我是经纪人,我替明星接下这个演出");
//执行目标对象方法
Object returnValue = method.invoke(target, args);
System.out.println("动态代理---我是经纪人,明星演出结束");
return returnValue;
}
}
);
}
}
也可以把InvocationHandler独立出来:
public static void main(String[] args) {
IActor star = new StarPeople();
// 给目标对象,创建代理对象
IActor proxy = (IActor) Proxy.newProxyInstance(star.getClass().getClassLoader(),
star.getClass().getInterfaces(), new MyInvocaionHandler(star));
proxy.sing();
proxy.dance();
}
class MyInvocaionHandler implements InvocationHandler{
private Object object;
public MyInvocaionHandler(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("动态代理前置操作");
Object returnValue = method.invoke(object, args);
System.out.println("动态代理后置操作");
return returnValue;
}
}
2.4.2、cglib代理
**** 利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。****
由于被代理类没有实现接口,所以运行时在内存中动态生成一个子类对象从而实现对目标对象的扩展。子类对象会重写目标对象的方法,所以目标对象不能为final类。
代理类和目标类都无需实现业务接口。
public class JingJiPenple implements MethodInterceptor{
Object target;
public JingJiPenple(Object target){
this.target = target;
}
//给目标对象创建一个代理对象
public Object getProxyInstance(){
//工具类
Enhancer en = new Enhancer();
//设置父类
en.setSuperclass(target.getClass());
//设置回调函数
en.setCallback((Callback) this);;
//创建子类(代理对象)
return en.create();
}
@Override
public Object intercept(Object arg0, Method method, Object[] arg2, MethodProxy proxy) throws Throwable {
System.out.println("Cglib代理---我是经纪人,我代明星接下演出!!!");
//执行目标对象的方法
Object returnValue = method.invoke(target, arg2);
System.out.println("Cglib代理---我是经纪人,明星演出结束!!!");
return returnValue;
}
}
2.5、Spring默认动态代理
Spring在5.X之前默认的动态代理实现一直是jdk动态代理。但是从5.X开始,Spring就开始默认使用****Cglib****来作为动态代理实现。
SpringBoot从2.X开始也转向了Cglib动态代理实现。
JDK动态代理要求接口,没有接口的情况下会报错。
而CGLIB作为默认动态代理,效果和JDK差不多,但是不会报错,更方便。
以上内容为个人学习汇总,仅供学习参考,如有问题,欢迎在评论区指出,谢谢!
版权归原作者 零点冰. 所有, 如有侵权,请联系我们删除。