面试整理-spring

ICO(控制反转)

        类的创建,销毁都由spring来控制,也就是说控制对象的生命周期的不再是引用它的对象,而是spring来控制整个过程。

        spring会通过BeanDefinitionReader来加载bean的配置信息。然后生成一个BeanDefinition,作为bean的定义信息,用来存储bean的所有属性方法定义;

BeanFactory

        BeanFactory是生产bean的工厂,负责生产和管理bean实例,同时也是spring容器暴露在外的获取bean的入口;BeanFactory的生产过程其实是利用反射机制实现的。

        BeanFactory与ApplicationContext :BeanFactory是一个底层的容器,而ApplicationContext是在其基础上增加了一些特性,比如更好的整合SpringAOP、国际化消息,事务,资源访问等。

Bean的生命周期:

    

spring创建bean的流程:

方法注入的方式:@Autowired,@Bean,@Resource,@Inject(jsr330中的规范)

在使用@Autowired时,如果有多个类型相同的bean注册到容器中,可以使用@Qualifier注解指定哪一个bean或者实现bean的逻辑分组,用法如下:

public class QualifierDemo {

    @Autowired
    private List<Demo> demos; // 1 ,2,3,4 全部都有

    @Autowired
    @Qualifier 
    private List<Demo> demosQualifier; // 只有 3,4

    @Autowired
    @Qualifier("demo2")
    private Demo demo1; // 只有2

    @Bean
    public Demo demo1() {
        return new Demo(1);
    }
    @Bean
    public Demo demo2() {
        return new Demo(2);
    }
    @Bean
    @Qualifier // 进行逻辑分组
    public Demo demo3() {
        return new Demo(3);
    }
    @Bean
    @Qualifier // 进行逻辑分组
    public Demo demo4() {
        return new Demo(4);
    }
    @Data
    public class Demo {
        private Integer id;
        public Demo (Integer id){
            this.id =id;
        }
    }
}

循环依赖

        A依赖B,B也依赖A;

spring解决循环依赖:

        前置条件:出现的循环依赖必须是单例,依赖注入的方式不能全是构造器注入;

        spring在创建bean的过程中分为三步,实例化(简单的new一个对象),属性注入(为实例化出来的对象填充属性),初始化(执行aware方法,初始化方法,完成AOP代理)。使用三级缓存:1、singletonObjects,一级缓存,存储的是所有创建好的bean;2、earlySingletonObjects,完成实例化,但是还未进行属性注入的及初始化的对象;3、singletonFactories,提前暴露一个单例工厂,二级缓存中存储的就是从这个工厂中获取到的对象;

总结spring解决循环依赖:

        spring通过三级缓存解决循环依赖,以及缓存为单例池,二级缓存为早期曝光对象,三级缓存为早期曝光对象工厂;当A、B两个类发生循环引用时,在A完成实例化后,就是用实例化后的对象建立一个工厂,并添加到三级缓存中,如果A被AOP代理,那么通过这个工厂可以获取到代理后的对象,如果没有被代理,那么这个工厂获取的就是A的实例化对象;当A进行属性注入时,去创建B,同时B又依赖了A,所以创建B的同时又会去调用getBean(a)来从缓存中获取需要的依赖,第一步先获取到三级缓存的工厂,第二步调用对象工厂的getObject方法获取到对应的对象,得到这个对象注入到B中,接着B会走完它的生命周期流程,包括初始化、后置处理等;当B创建完后,会将B再注入到A中,此时A再完成它的生命周期;

        二级缓存也可以解决循环依赖,但要意味着所有的bean在实例化后就要完成AOP代理,违背了spring设计的原则;

依赖情况依赖注入方式循环依赖是否被解决
AB相互依赖(循环依赖)均采用setter方法注入
AB相互依赖(循环依赖)均采用构造器注入
AB相互依赖(循环依赖)A中注入B的方式为setter方法,B中注入A的方式为构造器
AB相互依赖(循环依赖)B中注入A的方式为setter方法,A中注入B的方式为构造器

spring三级缓存为什么不能解决2、4情况?

        如果是构造器注入的话,A在实例化时会调用带参构造方法,此时会getBean(B),B同样没有实例化,需要执行带参构造方法,getBean(A),此时A仍没有实例化,这对于A来说是第二次执行这个流程,因此就会报错;第四种同样,在B需要获取A实例的时候,A仍未完成初始化。第三种可以解决因为spring在创建bean的时候会根据自然排序进行创建,所以A会先于B创建,A会先调用无参构造方法实例化,属性注入时实例化B,此时已经生成了A对象,只是还未属性注入,因此B可以调用带参构造方法实例化。

AOP

        面向切面编程,是一种编程范式,通过预编译的方式和运行期动态代理实现程序功能的统一维护;通常用来隔离不同的业务逻辑,比如常见的事务、日志管理等。实现动态代理的方式有两种:cglib和jdk动态代理。

AOP的核心概念:

        切面(Aspect):类似Java的类声明,一般使用@Aspect注解或<aop:aspect>定义切面;

        连接点(Join Point):程序执行中的特定点,如方法执行,处理异常等;

        切点(Pointcut):通过规则匹配的正则表达式,当有连接点可以匹配到切点时,就会触发切点相关的 指定通知。

        通知(Advice):在切面中某个连接点采取的动作(around, before, after, exception, return)

        一个应用可以有多种通知方式,所以在AOP中引入责任链设计模式,通过这种模式来顺序的执行每个通知,也可以用@Order注解,配置数字越小,越先执行;

使用方式:

@Aspect
@Component
public class LogUtils {

    @Pointcut("execution(public * com.sun.test.aop..*.*(..))")
    public void pcMethod(){

    }

    @Before("pcMethod()")
    public void before(){
        System.out.println("before");
    }

    @Around("pcMethod()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("around before");
        Object o = pjp.proceed();
        System.out.println("around after");
        return o;
    }

    @Around("pcMethod()")
    public Object around1(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("around1 before");
        Object o = pjp.proceed();
        System.out.println("around1 after");
        return o;
    }

    @After("pcMethod()")
    public void after(){
        System.out.println("after");
    }

}

源码解析:

private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
        private final AdvisedSupport advised;

        public DynamicAdvisedInterceptor(AdvisedSupport advised) {
            this.advised = advised;
        }

        @Nullable
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            Object oldProxy = null;
            boolean setProxyContext = false;
            Object target = null;
            TargetSource targetSource = this.advised.getTargetSource();

            Object var16;
            try {
                if (this.advised.exposeProxy) {
                    oldProxy = AopContext.setCurrentProxy(proxy);
                    setProxyContext = true;
                }

                target = targetSource.getTarget();
                Class<?> targetClass = target != null ? target.getClass() : null;
              // 从advised中 获取配置好的AOP的通知方法 -重点
                List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
                Object retVal;
              // 如果没有配置通知方法,则直接调用target对象的调用方法
                if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
                    Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                    retVal = methodProxy.invoke(target, argsToUse);
                } else {
               // 通过CglibMethodInvocation来启动advice通知 - 重点
                    retVal = (new CglibAopProxy.CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy)).proceed();
                }

                retVal = CglibAopProxy.processReturnType(proxy, target, method, retVal);
                var16 = retVal;
            } finally {
                if (target != null && !targetSource.isStatic()) {
                    targetSource.releaseTarget(target);
                }

                if (setProxyContext) {
                    AopContext.setCurrentProxy(oldProxy);
                }

            }

            return var16;
        }

        在DynamicAdvisedInterceptor中有一个List<Object> chain,这里获取到配置的通知方法;此时如果chain不为空就走到通过CglibMethodInvocation启动advice通知这一步流程中;

public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {

 protected final Object proxy;

 @Nullable
 protected final Object target;

 protected final Method method;

 protected Object[] arguments = new Object[0];

 @Nullable
 private final Class<?> targetClass;

 /**
  * Lazily initialized map of user-specific attributes for this invocation.
  */
 @Nullable
 private Map<String, Object> userAttributes;

 /**
  * List of MethodInterceptor and InterceptorAndDynamicMethodMatcher
  * that need dynamic checks.
  */
 protected final List<?> interceptorsAndDynamicMethodMatchers;

 /**
  * Index from 0 of the current interceptor we're invoking.
  * -1 until we invoke: then the current interceptor.
  */
 private int currentInterceptorIndex = -1;

 // 省略其他的方法

 @Override
 @Nullable
 public Object proceed() throws Throwable {
  // We start with an index of -1 and increment early.
    // 从索引为-1的拦截器还是调用,并且按顺序递增,如果整个List chain中调用完毕,则开始调用target的函数
    // 具体的实现方式在AOPUtils.invokeJoinpointUsingRefection方法中,说白了就是通过反射实现目标方法
  if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
   return invokeJoinpoint();
  }
  // 获取下一个需要执行的拦截器,沿着定义好的interceptorOrInterceptionAdvice的链进行处理
  Object interceptorOrInterceptionAdvice =
    this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
  if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
   // Evaluate dynamic method matcher here: static part will already have
   // been evaluated and found to match.
      // 对拦截器进行动态匹配,如果和定义的pointcut匹配 则就会执行当前的这个advice
   InterceptorAndDynamicMethodMatcher dm =
     (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
   Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
   if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
    return dm.interceptor.invoke(this);
   }
   else {
    // Dynamic matching failed.
    // Skip this interceptor and invoke the next in the chain.
    return proceed();
   }
  }
  else {
   // It's an interceptor, so we just invoke it: The pointcut will have
   // been evaluated statically before this object was constructed.
      // 普通拦截器,直接调用拦截器,将当前的this(CglibMethodInvocation)作为参数保证当前实例中调用链的执行
   return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
  }
 }

 // 省略其他的方法
}

        这里currentInterceptorIndex默认值为-1,相当于是作为判断当前通知链式是否执行完成,执行完就直接通过反射调用目标方法,否则向下继续执行;

        在上面的chain中,会比我们配置的多出一个方法,这个就是ExposeInvocationInterceptor这个方法;

public final class ExposeInvocationInterceptor implements MethodInterceptor, PriorityOrdered, Serializable {
 
 @Override
 public Object invoke(MethodInvocation mi) throws Throwable {
  MethodInvocation oldInvocation = invocation.get();
  invocation.set(mi);
  try {
      // 具体执行
   return mi.proceed();
  }
  finally {
   invocation.set(oldInvocation);
  }
 }
}

        引入这个设计的原因在于配置的方法可能很多,通过这个类作为第一个通知方法,暴露调用的拦截器,来保证所有的 通知方法按照链式执行完毕;

        在执行每一种通知方法时,都会有对应的切面通知方法,这些类都实现了MethodInterceptor,重写invoke()方法;

org.springframework.aop.aspectj.AspectJAfterAdvice
org.springframework.aop.aspectj.AspectJAfterReturningAdvice
org.springframework.aop.aspectj.AspectJAfterThrowingAdvice
org.springframework.aop.aspectj.AspectJAroundAdvice
org.springframework.aop.aspectj.AspectJMethodBeforeAdvice

spring中的五种通知,首先时通过spring容器的启动过程获取到具体的通知,在调用对象时,通过动态代理技术,把需要执行的advice全部放在一个chain链中,为了保证整个链的调用默认会先调用,会先调用ExposeInvocationInterceptor去触发整个链式执行,在执行完每一个advice后都会再回到super的proceed方法中,执行下一个advice,在执行不同的advice时会有对应的切面通知方法。

静态代理:

        

        Cilent是直接和Proxy打交道的,Proxy是Client要真正调用的ReadSubject的代理,它确实执行了RealSubject的request方法,不过在执行前后Proxy也加上了额外的preRequest(),afterRequest()方法,面向接口编程,对调用方无感;Proxy通过其属性持有真正要代理的目标对象以达到技能调用目标对象的方法,也能在方法前后注入其他逻辑的目的;

代码实现:

public class ProxyTest {

    interface Subject{
        void request();
    }
    static class RealSubject implements Subject{

        @Override
        public void request() {
            System.out.println("吃饭");
        }
    }

    static class Proxy implements Subject {

        private Subject subject;

        public Proxy(Subject subject) {
            this.subject = subject;
        }

        @Override
        public void request() {
            before();
            subject.request();
            after();
        }

        void before(){
            System.out.println("做饭");
        }

        void after(){
            System.out.println("刷碗");
        }
    }

    public static void main(String[] args) {
        RealSubject subject = new RealSubject();
        Proxy proxy = new Proxy(subject);
        proxy.request();
    }
}

JDK动态代理:

代码实现:

public class JdkProxyTest {

    interface Subject{
        void request();
    }

    static class RealSubject implements Subject{

        @Override
        public void request() {
            System.out.println("吃饭");
        }
    }

    static class ProxyFactory{

        private Object object;

        public ProxyFactory(Object object) {
            this.object = object;
        }

        public Object getProxyInstance(){
            return Proxy.newProxyInstance(object.getClass().getClassLoader(),
                    object.getClass().getInterfaces(),
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            System.out.println("开始");
                            method.invoke(object, args);
                            System.out.println("结束");
                            return null;
                        }
                    });
        }
    }

    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        Subject subject = (Subject) new ProxyFactory(realSubject).getProxyInstance();
        subject.request();
    }
}

主要方法:

public static Object newProxyInstance(ClassLoader loader,
                                         Class<?>[] interfaces,
                                         InvocationHandler h);

loader:代理类的ClassLoader,最终读取动态生成的字节码,并转成java.lang.Class类的一个实例,通过此实例的newInstance()方法创建代理对象;

interfaces:委托类实现的接口,JDK动态代理要实现委托类的接口;

InvocationHandler:委托对象所有的方法调用都会转发到InvocationHandler.invoke(),在invoke()方法我们可以加入任何需要增强的逻辑,主要是根据委托类的接口等通过反射生成的。

缺点:委托类必须要实现接口;

cglib动态代理:

        代码实现:

public class CglibProxyTest {

    static class RealSubject{
        public void request() {
            System.out.println("吃饭");
        }
    }

    static class MyMethodInterceptor implements MethodInterceptor{

        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("开始");
            //Object object = method.invoke(o, objects);
            Object object = methodProxy.invokeSuper(o, objects);
            System.out.println("结束");
            return object;
        }
    }

    public static void main(String[] args) {

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(RealSubject.class);
        enhancer.setCallback(new MyMethodInterceptor());
        RealSubject subject = (RealSubject) enhancer.create();
        subject.request();
    }
}

底层原理:

        主要利用Enhancer这个类来设置委托类与方法拦截器。通过继承委托类,重写委托类的非final方法(final方法不能被重写),并在方法里调用委托类的方法实现代码增强。

        cglib并不要求委托类实现任何接口,而且cglib是高效的代码生成包,底层依靠ASM(开源的Java字节码编辑类库)操作字节码实现,性能比JDK强。由于反射的效率比较低,所以cglib采用FastClass机制实现对被拦截方法的调用,FastClass机制就是对一个类的方法建立索引,通过索引直接调用相应的方法。

事务

        spring中事务的传播行为:

1、required:如果当前没有事务,则新建一个事务,如果当前存在事务则加入事务;默认;

2、supports:如果当前存在事务,则加入当前事务,如果当前没有事务,则以非事务方法执行;

3、mandatory:当前存在事务,则加入事务,当前不存在事务,则抛出异常;

4、requires_new:创建一个新事务,如果存在当前事务,则挂起当前事务;

5、not_supported:以非事务方式执行,如果存在事务,则挂起当前事务;

6、never:如果当前没有事务,就以非事务方式运行,有事务就抛出异常;

7、nested:如果当前存在事务,则嵌套在事务中执行,否则开启一个新事务;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值