1、IOC
概念:所谓控制反转,就是把原先我们代码里面需要实现的对象创建、依赖的代码,反转给容器来帮忙实现。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。
Spring 启动时读取应用程序提供的Bean配置信息,并在Spring容器中生成一份相应的Bean配置注册表,然后根据这张注册表实例化Bean,装配好Bean之间的依赖关系,为上层应用提供准备就绪的运行环境。
image.png1.1 底层实现:
image.png如果是xml文件,那么需要解析xml文件;如果是注解,需要通过反射获取注解,然后根据获取到的bean信息通过反射实例化bean,实例化之后将bean放到spring容器的bean缓存池中(hashMap),当要使用bean时,可以通过applicationContext获取bean(getBean)。
1.2 spring ioc autowired如何实现
@Autowired表示被修饰的类需要注入对象,spring会扫描所有被@Autowired标注的类,然后根据 类型type 在ioc容器中找到匹配的类注入
1.3 @component
普通pojo实例化到spring容器中,相当于配置文件中的<bean id="" class=""/>
虽然有了@Autowired,但是我们还是要写一堆bean的配置文件,相当麻烦,而@Component就是告诉spring,我是pojo类,把我注册到容器中吧,spring会自动提取相关信息。那么我们就不用写麻烦的xml配置文件了。
https://zhuanlan.zhihu.com/p/29344811
1.4 bean生命周期
image.png- 1.4.1.当调用者通过 getBean(beanName)向容器请求某一个 Bean 时,如果容器注册了org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor 接口,在实例化 Bean 之前,将调用接口的 postProcessBeforeInstantiation()方法;
- 1.4.2.根据配置情况调用 Bean 构造函数或工厂方法实例化 Bean;
- 1.4.3.如果容器注册了 InstantiationAwareBeanPostProcessor 接口,在实例化 Bean 之后,调用该接口的 postProcessAfterInstantiation()方法,可在这里对已经实例化的对象进行一些“梳妆打扮”;
- 1.4.4.如果 Bean 配置了属性信息,容器在这一步着手将配置值设置到 Bean 对应的属性中,不过在设置每个属性之前将先调用InstantiationAwareBeanPostProcessor 接口的postProcessPropertyValues()方法;
- 1.4.5.调用 Bean 的属性设置方法设置属性值;
- 1.4.6.如果 Bean 实现了 org.springframework.beans.factory.BeanNameAware 接口,将调用setBeanName()接口方法,将配置文件中该 Bean 对应的名称设置到 Bean 中;
- 1.4.7.如果 Bean 实现了 org.springframework.beans.factory.BeanFactoryAware 接口,将调用 setBeanFactory()接口方法,将 BeanFactory 容器实例设置到 Bean 中;
- 1.4.8.如果 BeanFactory 装配了 org.springframework.beans.factory.config.BeanPostProcessor后处理器,将调用 BeanPostProcessor 的 Object postProcessBeforeInitialization(Object bean, String beanName)接口方法对 Bean 进行加工操作。其中入参 bean 是当前正在处理的 Bean,而 beanName 是当前 Bean 的配置名,返回的对象为加工处理后的 Bean。用户可以使用该方法对某些 Bean 进行特殊的处理,甚至改变 Bean 的行为, BeanPostProcessor 在 Spring 框架中占有重要的地位,为容器提供对 Bean 进行后续加工处理的切入点, Spring 容器所提供的各种“神奇功能”(如 AOP,动态代理等)都通过 BeanPostProcessor 实施;
- 1.4.9.如果 Bean 实现了 InitializingBean 的接口,将调用接口的 afterPropertiesSet()方法;
- 1.4.10.如果在<bean>通过 init-method 属性定义了初始化方法,将执行这个方法;
- 1.4.11.BeanPostProcessor 后处理器定义了两个方法:其一是 postProcessBeforeInitialization() 在第 8 步调用;其二是 Object postProcessAfterInitialization(Object bean, String beanName)方法,这个方法在此时调用,容器再次获得对 Bean 进行加工处理的机会;
- 1.4.12.如果在<bean>中指定 Bean 的作用范围为 scope=“prototype”,将 Bean 返回给调用者,调用者负责 Bean 后续生命的管理, Spring 不再管理这个 Bean 的生命周期。如果作用范围设置为 scope=“singleton”,则将 Bean 放入到 Spring IoC 容器的缓存池中,并将 Bean引用返回给调用者, Spring 继续对这些 Bean 进行后续的生命管理;
- 1.4.13.对于 scope=“singleton”的 Bean,当容器关闭时,将触发 Spring 对 Bean 的后续生命周期的管理工作,首先如果 Bean 实现了 DisposableBean 接口,则将调用接口的afterPropertiesSet()方法,可以在此编写释放资源、记录日志等操作;
- 1.4.14.对于 scope=“singleton”的 Bean,如果通过<bean>的 destroy-method 属性指定了 Bean 的销毁方法, Spring 将执行 Bean 的这个方法,完成 Bean 资源的释放等操作。
可以将这些方法大致划分为三类:
-
Bean 自身的方法:如调用 Bean 构造函数实例化 Bean,调用 Setter 设置 Bean 的属性值以及通过<bean>的 init-method 和 destroy-method 所指定的方法;
-
Bean 级生命周期接口方法:如 BeanNameAware、 BeanFactoryAware、 InitializingBean 和 DisposableBean,这些接口方法由 Bean 类直接实现;
-
容器级生命周期接口方法:在上图中带“★” 的步骤是由 InstantiationAwareBean PostProcessor 和 BeanPostProcessor 这两个接口实现,一般称它们的实现类为“ 后处理器” 。 后处理器接口一般不由 Bean 本身实现,它们独立于 Bean,实现类以容器附加装置的形式注册到 Spring 容器中并通过接口反射为 Spring 容器预先识别。当Spring 容器创建任何 Bean 的时候,这些后处理器都会发生作用,所以这些后处理器的影响是全局性的。当然,用户可以通过合理地编写后处理器,让其仅对感兴趣Bean 进行加工处理。
ApplicationContext 和 BeanFactory 的不同之处在于:ApplicationContext会利用 Java 反射机制自动识别出配置文件中定义的 BeanPostProcessor、 InstantiationAwareBeanPostProcessor 和 BeanFactoryPostProcessor,并自动将它们注册到应用上下文中;而后者需要在代码中通过手工调用 addBeanPostProcessor()方法进行注册。这也是为什么在应用开发时,我们普遍使用 ApplicationContext 而很少使用 BeanFactory 的原因之
2、AOP
2.1 概念
AOP的全称是Aspect Orient Programming,即面向切面编程,扩展功能不通过修改源代码实现。
2.2实现方式
-
2.2.1 JDK 动态代理(必须有接口)
通过java.lang.reflect.Proxy类实现。
动态代理就是为了解决静态代理不灵活的缺陷而产生的。静态代理是固定的,一旦确定了代码,如果委托类新增一个方法,而这个方法又需要增强,那么就必须在代理类里重写一个带增强的方法。而动态代理可以灵活替换代理方法,动态就是体现在这里。同时,静态代理每个方法都需要单独写一个代理类,而动态代理一个接口只实现一个动态代理类即可。 -
2.2.2 实现
Moveable move = (Moveable) Proxy.newProxyInstance(Car.class.getClassLoader(), Car.class.getInterfaces(), new LogHandler(new Car()));
使用JDK的动态代理去生成代理只需要一行代码,传入的参数中其实就俩,一是被代理类的类对象,二是自定义的增强处理代码。
从上面的例子中可以看出,动态代理除了接受Car类型的目标对象,还可以接受任何其他类型的对象;也不管目标对象实现的接口有多少方法,都可以被代理。
public class LogHandler implements InvocationHandler{
private Object target;
public LogHandler(Object object){
super();
this.target = object;
}
//增强处理
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object o = method.invoke(target,args);
return o;
}
}
image.png
- 2.2.2 cglib 动态代理(不需要类继承任何接口,字节码技术)
public class Plane {
public void fly(long ms) {
System.out.println("plane is flying!");
try {
Thread.sleep(ms);
} catch (Exception e) {
}
}
}
public class CglibProxy implements MethodInterceptor {
private Object target;
public CglibProxy(Object target) {
this.target = target;
}
public Object getProxyInstance() {
//1. 实例化工具类
Enhancer en = new Enhancer();
//2. 设置父类对象
en.setSuperclass(this.target.getClass());
//3. 设置回调函数
en.setCallback(this);
//4. 创建子类,也就是代理对象
return en.create();
}
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before invoke ");
long begin = System.currentTimeMillis();
//执行目标对象的方法
Object returnValue = method.invoke(target, objects);
long end = System.currentTimeMillis();
System.out.println("after invoke elpased " + (end - begin));
return returnValue;
}
}
public static void main() {
CglibProxy cglibProxy = new CglibProxy(new Plane());
Plane plane = (Plane) cglibProxy.getProxyInstance();
plane.fly(150);
}
2.4使用场景:
- Logging 日志
- Authentication 权限
- Transactions 事务
- Context passing 内容传递
- Error handling 错误处理
- Lazy loading 懒加载
- Debugging 调试
- logging,tracing,profiling and monitoring 记录跟踪 优化 校准
- Performance optimization 性能优化
- Persistence 持久化
- Resource pooling 资源池
- Synchronization 同步
- Caching 缓存
2.5 在Spring的AOP编程中
如果加入容器的目标对象有实现接口,就使用JDK代理
如果目标对象没有实现接口,就使用Cglib代理。
3、反射:
反射是Java的特征之一,是一种间接操作目标对象的机制,核心是JVM在运行的时候才动态加载类,并且对于任意一个类,都能够知道这个类的所有属性和方法,调用方法/访问属性,不需要提前在编译期知道运行的对象是谁,他允许运行中的Java程序获取类的信息,并且可以操作类或对象内部属性。
程序中对象的类型一般都是在编译期就确定下来的,而当我们的程序在运行时,可能需要动态的加载一些类,这些类因为之前用不到,所以没有加载到jvm,这时,使用Java反射机制可以在运行期动态的创建对象并调用其属性,它是在运行时根据需要才加载。
3.1 new和反射创建有什么区别
new:静态编译,在编译期就将模块编译进来,执行该字节码文件,所有的模块都被加载;
反射:动态编译,编译期没有加载,等到模块被调用时才加载;
3.2 反射的作用
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法;
- 在运行时调用任意一个对象的方法;
3.3 反射的实现
要使用一个类,就要先把它加载到虚拟机中,生成一个Class对象。这个class对象就保存了这个类的一切信息。
反射机制的实现,就是获取这个Class对象,通过Class对象去访问类、对象的元数据以及运行时的数据。
//通过反射机制创建class对象
class1 = Class.forName(className);
//在运行时,通过创建的class对象,获取自己的父类信息
Class<?> parentClass = class1.getSuperclass();
//通过反射机制创建一个类的对象
Classname 对象=class1.newInstance(参数);
//取得本类已声明的所有字段,包括私有的、保护的
Field[] field = class1.getDeclaredFields();
//返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
Method method = clazz.getMethod(方法名,参数类型);
//调用具体某个实例对象的这个公有方法
method.invoke(实例对象,参数值);
网友评论