Spring中的ioc和aop

发布时间:2024年01月10日


说起这个 java中离不开的spring全家桶,基本都会问。问题是这么多年了,同样的问题,是否有更多的理解和感触,是否能把这个问题回答的更清晰。当初你走过的路,你爱过的人,你经历过的事,都会藏在你的气质里, 而对于面试包含你对整个技术的理解,傻傻的背着面试题只会让你的面试通过率更低。

spring框架解决了什么问题

如果没有spring,我们正常的MVC结构的开发就需要自己去接收http请求,自己去负责整合各种jar包,orm框架你需要自己去对接,自己去连接数据库实例等等,而这一切目前我们都是交给Spring去管理,我们只关注于业务开发。Spring可以快速的搭建一个开发环境并且部署,只需要一个Spring initializr,就可以快速的构建,使用restController可以接收http请求并处理,搭配上ORM框架就是一个完整的开发。
java是一个面向对象编程,我们所有的开发都是在处理各种对象,而对象之间的依赖关系错综复杂,所有的对象使用之前都需要new实例化的,而现实开发中其实我们很少去new实例,原因就是spring通过ioc和di机制,实现了对象的自动依赖,我们直接使用就可以。spring就是一个大的工厂模式,将所有的实例管理起来,方便我们使用,spring还解决了**循环依赖**问题,那同一个接口,在不同的地方调用的话这个bean是单例的么?

Spring 的优点

通过控制反转(IoC)和依赖注入(DI)机制,实现了对象之间的自动依赖注入,提供了aop能力,提供了统一的事务管理方式,简化了WEB应用开发,方便各种框架的集成。

IOC和DI

ioc就是Inversion of Control,就是控制反转。因为我们对象需要new,将这个手动创建对象的控制权交给spring的BeanFactory工厂去管理对象bean的生命周期,这就是控制反转。
DI就是依赖注入。指的是应用程序运行时依赖ioc容器动态注入所需环境,换句话说就是指对象不是从容器中查找它依赖的类,而是在容器实例化对象的时候主动将它依赖的类注入给它。底层是通过反射实现注入的。我们通常是使用@service等注解将对象教由spring管理,通过@autowired实现依赖注入使用对象。
可以参考这个ioc和DI 有伪代码讲解 我就不贴代码了

aop

面对对象编程是一个纵向的继承关系,而如果有一些扩展的话,就需要使用装饰器模式去添加一些新的功能,aop就是为了解决这种问题存在的。常用的我们会使用aop将接口的参数打印出来,提供给以后得问题分析使用,可以记录接口时间等等,通过 AOP 补充他们对 OOP 的使用

ioc的原理

ioc是设计思想,spring有一个顶层接口BeanFactory,这个接口定义了Bean的创建、配置、初始化和销毁等基本操作,他的实现由AbstractApplicationContext,ApplicationContext等,bean的管理就交由他们处理,基本包含三种 XML配置方式、Java配置方式和注解配置方式。

public interface BeanFactory {
	
	//如果需要得到工厂本身,需要转义
	String FACTORY_BEAN_PREFIX = "&";

	//根据bean的名字,获取在IOC容器中得到bean实例
	Object getBean(String name) throws BeansException;

	//根据bean的名字和Class类型来得到bean实例,增加了类型安全验证机制。
	<T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;

	Object getBean(String name, Object... args) throws BeansException;

	<T> T getBean(Class<T> requiredType) throws BeansException;

	<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

	//提供对bean的检索,看看是否在IOC容器有这个名字的bean
	boolean containsBean(String name);

	//根据bean名字得到bean实例,并同时判断这个bean是不是单例
	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

	boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

	boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

	//得到bean实例的Class类型
	@Nullable
	Class<?> getType(String name) throws NoSuchBeanDefinitionException;

	//得到bean的别名,如果根据别名检索,那么其原名也会被检索出来
	String[] getAliases(String name);
}

di的原理

通过autowired实现自动注入,源码分析 先贴一段别人的源码分析,后面自己把这块弄明白了再补上自己的
autowired和resource的不同
贴一段代码

AbstractAutowireCapableBeanFactory 的 populateBean()方法

 // Add property values based on autowire by name if applicable.
            //根据Bean名称进行autowiring自动装配处理
            if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
                autowireByName(beanName, mbd, bw, newPvs);
            }

            // Add property values based on autowire by type if applicable.
            //根据Bean类型进行autowiring自动装配处理
            if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
                autowireByType(beanName, mbd, bw, newPvs);
            }

几种常见的依赖注入方式:
以下是几种常见的使用方式:

字段注入:最常用

@Autowired
private Dependency dependency;

构造函数注入:

private Dependency dependency;

@Autowired
public MyClass(Dependency dependency) {
    this.dependency = dependency;
}

Setter 方法注入:

private Dependency dependency;

@Autowired
public void setDependency(Dependency dependency) {
    this.dependency = dependency;
}

aop的原理

一段简单的aop代码,作用是打印接口的所有参数。

@Service
public class ItemServiceAspect {
   // 切点表达式,匹配 ItemService 接口的所有方法
   @Pointcut("execution(* com.example.demo.service.ItemService.*(..))")
   public void itemServicePointcut() {}

   @Before("itemServicePointcut()")
   public void beforeItemService(JoinPoint joinPoint) {
       Object[] args = joinPoint.getArgs();
       if (args != null && args.length > 0) {
           for (Object arg : args) {
               System.out.println("参数值:" + arg.toString());
           }
       }
   }
}

aop就是代理模式的体现,就是在不修改原有代码的基础上增加新的功能。主要有三种实现方式,静态代理,jdk动态代理,cglib代理。
我自己写了两个代码示例 git地址

https://gitee.com/sunweibo5/demo/tree/master/src/main/java/com/example/demo/service/proxy

静态代理,jdk动态代理,cglib代理的区别

对于实现了接口的代理,可以有静态代理或者动态代理。
cglib可以对class文件代理,通过反射。
静态反射限定了必须要有明确的接口类,每个目标对象都得有一个代理。
JDK动态代理是基于Java反射机制实现的,它要求目标类必须实现一个或多个接口,代理对象在运行时动态创建,通过实现目标类接口的方式来代理目标类。 CGLIB代理则是基于ASM字节码框架实现的,它可以代理没有实现接口的目标类。被代理类加载之前就完成了代理类的创建,基于继承的方式对被代理类生成子类。因为它是继承了被代理类,所以它会受到final类、private、static等不可继承属性的影响

public class Proxy implements MethodInterceptor {

    private Object demo;
    public Proxy(Object demo){
        this.demo = demo;
    }
    @Override
    //   o是代理对象的实例   method可以查看实例的方法  args是参数  methodProxy是MethodProxy 对象,用于调用目标方法
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        if (!method.getName().equals("interview")) {
            return null;
        }
        System.out.println("只一次先在别的地方试试,以后有你哭的时候");
        Object result = methodProxy.invokeSuper(o,args);
        System.out.println("没有办法了再去中团呆着 ");
        return result;
    }

    public static Demo createProxy(){
        Enhancer en = new Enhancer();
        en.setSuperclass(Demo.class);
        en.setCallback(new Proxy(new Demo()));
        Demo demo = (Demo)en.create();
        return demo;
    }


    public static void main(String[] args) {
        Demo demo = Proxy.createProxy();
        demo.interview("zhongruan");
    }
}

jdk动态代理可以创建一个代理对象。当代理对象调用其实现的接口方法时,方法调用将被重定向到InvocationHandler对象的invoke()方法 .JDK动态代理利用反射机制生成一个包含被代理对象的所有接口的代理类并覆盖接口中的所有方法,举一个简单的反射的例子

Class<?> clazz = MyClass.class;
Object obj = clazz.newInstance();

通过反射可以获取当前对象的实例,可以获取对象的构造方法,接口等等。而Proxy.newProxyInstance就是一个典型的反射获取实例的方法。

public class ProxyInvocationHandler implements InvocationHandler {


    private Object target;
	
    public ProxyInvocationHandler(Object target) {
        this.target = target;
    }

    public Object getProxy(){
    //
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("在 add 方法执行前");
        //动态代理的本质,就是使用反射机制实现;
        Object result = method.invoke(target, args);
        System.out.println("在 add 方法执行后");
        return result;
    }

    public static void main(String[] args) {
        UserService userService =new UserServiceImpl();
        ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler(userService);
        UserService userServiceProxy = (UserService) proxyInvocationHandler.getProxy();
        userServiceProxy.add();
    }

}

spring是单例的么

spring中的对象默认是单例的,所以在ioc容器中只有一个存在。所以一般在开发接口的过程中,不使用成员变量,如果需要有成员变量的话使用threadLocal修饰,或者设置为原型模式。

@Service
@Scope(value = BeanDefinition.SCOPE_PROTOTYPE)
public class ItemServiceImpl implements ItemService {

Spring支持五个作用域:singleton、prototype、request、session、global session

1.singleton:默认作用域Spring IOC容器仅存在一个Bean实例,Bean以单例方式存在,在创建容器时就同时自动创建了一个Bean对象。作用域范围是ApplicationContext中。

2.prototype:每次从容器中调用Bean时,都会返回一个新的实例,即每次调用getBean时。作用域返回是getBean方法调用直至方法结束。

相当于执行newXxxBean().Prototype是原型类型,再我们创建容器的时候并没有实例化,而是当我们获取Bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。

3.request:每次HTTP请求都会创建一个新的Bean,作用域范围是每次发起http请求直至拿到相应结果。该作用域仅适用于WebApplicationContext环境。

4.session:首次http请求创建一个实例,作用域是浏览器首次访问直至浏览器关闭。

同一个HTTP Session共享一个Bean,不同Session使用不通的Bean,仅适用于WebApplicationContext环境。

5.global-session:作用域范围是WebApplicationContext中。一般用于Portlet应用环境,该运用域仅适用于WebApplicationContext环境。

spring的bean的生命周期

其实想从源码分析一下的,发现自己不够扎实,先贴上一段别人的分析吧。还是对BeanFactory的扩展解释 bean的生命周期

spring应用了什么设计模式

spring使用了特别多的设计模式,我们可以学习,上面提到的工厂模式,原型模式,单例模式,代理模式,装饰器模式等

文章来源:https://blog.csdn.net/qq_41949481/article/details/135474106
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。