两年前刚开始学习Java Spring框架的时候,从学习资料中了解到IOC和AOP是Spring的精髓,然而两年过去,在即将毕业找实习的过程中,反复被面试官问到我对其的理解,长时间没有复习,除了记住“IOC和AOP在Spring中很重要”外,其他东西早就忘光了。主要原因还是初学的时候就没有掌握其精髓、没有深入理解。本文通过一些常见的面试题重新温习Spring IOC和AOP。
IOC和AOP面试常见问题
1.IOC是什么,以及有什么用?
IOC (Inversion of control )控制反转/反转控制的思想,而DI(Dependency Injection)是IOC思想的一种技术实现。所谓控制反转,就是将创建、管理对象的权利转移给外部框架(如Spring IOC容器),从而使得调用者和被调用者之间的代码耦合度降低;例如通过Spring IOC容器很简单就可以实现一个单例而无需专门去将对象的实现改成单例的getInstance方法返回的方式。
ps:进一步问为什么IOC容器管理对象就会比new一个对象好?
a.在不是必须一个对象多个实例的场景下,如果每次使用一次就new一下,则可能导致大量的对象创建和销毁的过程,比较耗费资源。而IOC容器中的对象默认是单例的,不回频繁创建与销毁。
b.更重要的是Spring 通过BeanFactory管理bean可以在bean的各个创建过程做一些额外的处理,实现对bean在创建的过程的管理,Spring将这些阶段通过各种接口暴露给我们,让我们可以对bean进行各种处理,我们只要让bean实现对应的接口,那么spring就会在bean的生命周期调用我们实现的接口来处理该bean。
2.IOC底层原理?
IOC底层原理:xml解析、工厂模式、反射。降低耦合度演变过程:原始模式->工厂模式->IOC过程。使用反射可以将被调用者和工厂类的耦合关系转换为被调用者和配置文件之间的关系。
3.IOC容器?
IOC接口,IOC思想基于IOC容器完成,IOC容器底层就是对象工厂。Spring提供IOC容器两种实现方式:(两个接口)
- BeanFactory:IOC接口基本实现,是Spring内部使用的接口,不提供给开发人员进行使用,特点:加载配置文件时不会创建对象,在获取(使用)对象时才去创建对象。
- ApplicationContext:BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员进行使用。特点:加载配置文件时就把配置文件中的对象进行创建。(推荐使用)
-
ApplicationContext接口的实现类:
- FileSystemXMLApplicationContext类(需要写配置文件的全路径)
- ClassPathXMLApplicationContext(配置文件在src下的相对路径)
-
4.依赖注入?
a.手动装配
手动装配常见的方式是在xml文件中手动配置bean对象的属性,主要分为三种方式:set注入、有参构造注入和接口注入,前两者比较常见。
b.自动装配
自动装配常见的方式是通过AutoWired注解,分为byType、byName、constructor
三种方式。
注解Autowired默认使用byType来自动装配,如果存在类型的多个实例就尝试使用byName匹配,如果通过byName也确定不了,可以通过Primary和Priority注解来确定。
5.bean的生命周期?
6.AOP是什么以及有什么用?
AOP(Aspect oriented programming)面向切面编程,在不改变原有业务逻辑的情况下,增强横切逻辑代码,根本上解耦合,避免横切逻辑代码重复。底层通过动态代理实现,在目标类实现了接口的情况下,使用JDK动态代理;在没实现接口的情况下,使用CGLib动态代理。
7.JDK动态代理
步骤:
- 创建接口,定义目标类要完成的功能(必须有接口)
- 创建目标类实现接口 (目标)
- 创建InvocationHandler接口的实现类,在invoke方法中完成代理类的功能(代理)
- 增强功能
- 调用目标方法
- 使用Proxy类的静态方法,创建代理对象,并把返回值转换成接口类型(客户端)
//目标类
public class UsbKingFactory implements UsbSell {
@Override
public float sell() {
System.out.println("动态代理方式__目标类中的方法调用");
return 85.0f;
}
}
//静态代理需要需要代理类去实现接口,而动态代理通过以下方式实现了代理角色,从而与接口解耦
public class Myhandler implements InvocationHandler {
Object target = null;
public Myhandler(Object object) {
this.target = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res = null;
res = method.invoke(target,args);//执行原本的目标方法
//以下是增强功能
if(res != null) {
Float price = (Float) res;
res = price + 25;
}
System.out.println("淘宝商家,给你返回一个优惠券,或者红包");
return res;
}
}
//客户端
public class User {
public static void main(String[] args) {
//创建代理对象,使用Proxy
//1. 创建目标对象
// UsbKingFacotry factory = new UsbKingFactory();
UsbSell factory = new UsbKingFactory();//目标对象
//2.创建InvocationHandler对象
InvocationHandler handler = new Myhandler(factory);
//3.创建代理对象,等同于静态代理中的TaoBao taoBao=new TaoBao()
UsbSell proxy = (UsbSell) Proxy.newProxyInstance(factory.getClass().getClassLoader(),
factory.getClass().getInterfaces(),
handler);
//com.sun.proxy.$Proxy0 : 这是jdk动态代理创建的对象类型。
System.out.println("proxy:" + proxy.getClass().getName());
//4.通过代理执行方法
float price = proxy.sell();
System.out.println("通过动态代理对象,调用方法:" + price);
}
}
8.CGLib动态代理
CGLib是一个开源的,强大的,高性能,高质量的Code生成类库。
步骤
- 导入依赖
- 创建Enhancer实例
- 设置superclass(目标类.class)
- 设置callback传入handler继承MethodInterceptor重写intercept方法。
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
public class SampleClass {
static CglibTest target;
public void test(){
System.out.println("hello world");
}
public static void main(String[] args) {
target = new CglibTest();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(SampleClass.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("before method run...");
Object result = proxy.invokeSuper(target, args);
System.out.println("after method run...");
return result;
}
});
SampleClass sample = (SampleClass) enhancer.create();
sample.test();
}
}