写点什么

喝完可乐桶后程序员回归本源,开源 Spring 基础内容

用户头像
小Q
关注
发布于: 2020 年 12 月 05 日

周六了,又是摸鱼的一天,今天还有点不在状态,脑瓜子迷迷糊糊的,昨晚出去喝可乐桶喝的脑子到现在都不是很正常(奉劝各位可以自己小酌:450ml 威士忌+1L 多一点可乐刚刚好,可能是我酒量不好),正好没啥事就想整理一下自己的文件夹,发现了很久之前整理的一个 spring 基础的思维导图,如下:



今天,就以这份思维导图为基础,讲解一下 spring 基础的内容,好了,我们来看一下文字和代码的详细解析吧


需要这份思维导图的,可以关注公众号:Java 架构师联盟,后台回复 Java 即可


什么是 Spring


spring 是一个轻量级的控制反转(ioc)和面向切面编程(AOP)的容器框架。


  • 轻量:从大小与开销两方面而言 Spring 都是轻量的。完整的 Spring 框架可以在一个大小只有 1MB 多的 jar 文件里发布;并且 Spring 所需的处理开销也是微不足道的。

  • 非入侵:在应用中,一般不需要引用 springjar 包里的类

  • 控制反转:Spring 的核心机制,通过控制反转技术促进了松耦合。简单来讲,就是把对象创建的权力交给了容器,让容器来管理对象。

  • 面向切面:允许通过分离应用的业务逻辑与系统级服务进行内聚性的开发。AOP 是基于代理的,通俗一点就是把 核心业务 和 系统服务(日志系统、权限管理等) 分开。


Spring 的核心配置和类


  1. applicationContext.xml:核心配置文件。作用:用于配置所有的类,这些类可以称为 springbean

  2. BeanFactory:容器的工厂类(接口)。作用:用于创建或获取 springbean,即 spring 管理的对象。

  3. ApplicationContext:应用上下文(接口)他是 BeanFactory 的子类 作用:用于创建或获取 springbean。功能比 BeanFactory 强大。BeanFactory ApplicationContext 的区别: BeanFactory:懒加载 需要某个对象再去加载 ApplicationContext:非懒加载 一次性加载所有的对象


Spring IOC


控制反转:把对象的创建、销毁的权利交给容器框架,由容器来管理对象的生命周期。ioc 不是新的技术,只是一种思想或理念,实现松耦合。


IOC 包括依赖注入(DI,核心) 和 依赖查找。


DI:依赖注入,简单来讲就是在 spring 实例化对象的时候,由容器来设置这些对象的属性。


spring 的注入方式


属性的注入(set 方法注入)


前提要有对应的 setter 方法


以下为 spring 配置文件代码 java bean 忽略。


<bean id="person" class="com.xx.Person">
复制代码


    <property name = "name" value = "xzy"/>
复制代码


    <property name = "coll">
复制代码


        <list>
复制代码


            <value>list</value>
复制代码


            <value>list2</value>
复制代码


        </list>
复制代码


    </property>
复制代码


    <property name = "map">
复制代码


        <map>
复制代码


            <entry key = "age" value = "21" />
复制代码


        </map>
复制代码


    </property>
复制代码


    <property name = "arr">
复制代码


        <array>
复制代码


            <value>java</value>
复制代码


            <value>C++</value>
复制代码


        </array>
复制代码


    </property>
复制代码


</bean>
复制代码

通过构造器注入


需要构造方法,javaBean 代码:


public class User {
复制代码


    private String name;
复制代码


    private Integer age;
复制代码


    public User(String name,Integer age){
复制代码


        this.name = name;
复制代码


        this.age = age;
复制代码


    }
复制代码


}
复制代码

配置文件代码:


<!--通过构造器的方式-->
复制代码


<bean id = "user" class = "cn.pojo.User">
复制代码


    <constructor-arg value = "Jay" ></constructor-arg>
复制代码


    <constructor-arg value = "21" />
复制代码


</bean>
复制代码


<!--指定下标的方式-->
复制代码


<bean id="user" class="cn.pojo.User">
复制代码


        <constructor-arg value="44" index="1"/>
复制代码


        <constructor-arg value="Jack" index="0"/></bean>
复制代码


<!--指定在构造中的参数名称-->
复制代码


<bean id = "user" class = "cn.pojo.User">
复制代码


    <constructor-arg value="44" name="age" />
复制代码


    <constructor-arg value="xxx" name = "name" />
复制代码


</bean>
复制代码

注入其他类


<!--通过构造器注入-->
复制代码


<bean id = "user" class = "com.xx.User">
复制代码


    <constructor-arg value="Jack"></constructor-arg>
复制代码


    <constructor-arg value="44"></constructor-arg>
复制代码


    <!--引用的方式 设置引用id-->
复制代码


    <property name = "car" ref = "car"></property>
复制代码


</bean>
复制代码


<bean id = "car" class = "com.xx.Car">
复制代码


    <property name = "type" value = "BWM"></property>
复制代码


</bean>
复制代码


复制代码


<!--内部声明,用这种方式声明,别的bean不能引用了-->
复制代码


<property name = "car">
复制代码


    <bean class = "cn.xx.Car">
复制代码


        <property name = "type" value = "红旗"/>
复制代码


    </bean>
复制代码


</property>
复制代码

bean 元素中的属性


  • id:Bean 的唯一标识符

  • name:通过 name 对 Bean 进行管理和配置 name 可以多个每个以逗号隔开。

  • class:指定了 Bean 的具体实现类,必须是完整的类名 实用类的全限定名

  • scope:设定 Bean 实例的作用域,其属性有 singleton(单例)、prototype(原型)、request、session、和 globalSession,默认值为 singleton.

  • constructor-arg:的子元素,可以传入构造参数实例化 该元素 index 属性指定构造参数的序号(从 0 开始).

  • property:的子元素,通过调用 Bean 实例中的 setter 方法完成属性赋值.

  • ref:property、constructor-arg 等元素的子元素,该元素中的 bean 属性用于指定对 Bean 工厂中某个 Bean 实例的引用;

  • value:property、constructor-arg 等元素的子元素,用来直接指定一个常量值;

  • list:用于封装 List 或数组类型的依赖注入。

  • set:用于封装 Set 或数组类型的依赖注入。

  • map:用于封装 Map 或数组类型的依赖注入。

  • entry:map 元素的子元素 用于设定一个键值对。


Bean 的实例化


构造器实例化


Spring 容器通过 Bean 对应的默认构造函数来实例化 Bean。


静态工厂方式实例化


通过创建静态工厂的方式来创建 Bean 的实例


public class BeanFactory {
复制代码


    public static Bean createBean(){
复制代码


        return new Bean();
复制代码


    }
复制代码


}
复制代码


<!--factory-method-->
复制代码


<bean id = "bean" class = "com.xx.BeanFactory" factory-method = "createBean"></bean>
复制代码

实例工厂化方式实例化


不再使用静态方法创建 Bean 实例,而是采用直接创建 Bean 实例的方式.


public class BeanFactory {
复制代码


    public BeanFactory(){
复制代码


        System.err.println("BeanFactory 工厂实例化")
复制代码


    }
复制代码


    public static Bean createBean(){
复制代码


        return new Bean();
复制代码


    }
复制代码


}
复制代码


复制代码


<!--配置工厂-->
复制代码


<bean id = "beanFactory" class = "com.xx.BeanFactory" /><!--factory-bean 属性指向配置的实例工厂;factory-method属性确定使用工厂的哪个方法-->
复制代码


<bean id = "bean" factory-bean="beanFactory" factory-method="createBean"/>
复制代码

Bean 的作用域


singleton:单例模式


Spring IOC 容器中只会存在一个共享的 Bean 实例,无论有多少个 Bean 引用它,始终指向同一对象。


配置文件:
复制代码


<bean id ="dog" class = "com.bean.Dog" scope="singleton"></bean>
复制代码


复制代码


java代码:
复制代码


public void test(){
复制代码


    ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
复制代码


    Dog dog1 = (Dog) ctx.getBean("dog");
复制代码


    System.err.println(dog1); 
复制代码


    Dog dog2 = (Dog) ctx.getBean("dog");
复制代码


    System.err.println(dog2);
复制代码


}
复制代码


复制代码


// 输出的结果一致,表明为单例模式。
复制代码

prototype:原型模式


每次通过 Spring 容器获取 prototype 定义的 bean 时,容器都将创建一个新的 Bean 实例,每个 Bean 实例都有自己的属性和状态


request


在一次 Http 请求中,容器会返回该 Bean 的同一实例。而对不同的 Http 请求则会产生新的 Bean,而且该 bean 仅在当前 HttpRequest 内有效。针对每一次 Http 请求,Spring 容器根据该 bean 的定义创建一个全新的实例,且该实例仅在当前 Http 请求内有效,而其它请求无法看到当前请求中状态的变化,当当前 Http 请求结束,该 bean 实例也将会被销毁。


session


在一次 Http Session 中,容器会返回该 Bean 的同一实例。而对不同的 Session 请求则会创建新的实例,该 bean 实例仅在当前 Session 内有效。同 Http 请求相同,每一次 session 请求创建新的实例,而不同的实例之间不共享属性,且实例仅在自己的 session 请求内有效,请求结束,则实例将被销毁。


Bean 的装配方式


基于 XML 的装配


两种装配方式:setter 注入和构造器注入。


设置注入的两个要求:


  • Bean 类必须提供一个默认的午餐构造方法

  • Bean 类必须为需要注入的属性提供对应的 setter 方法


基于注解(annotation)


常见注解:


  • @Component 是所有受 Spring 管理组件的通用形式。

  • @Repository 持久层使用,dao 层使用。

  • @Service 业务类,表示业务层组件,Service 层使用。

  • @Controller 控制器,表示 web 层组件

  • @Autowired 按照类型来装配

  • @Resource 根据名字去装配


自动装配


属性值说明语法 default 默认值由的 default-autowire 属性值确定 default-autowire=“default”byName 根据属性名称自动装配 byType 根据属性的数据类型自动装配 constructor 根据构造函数参数的数据类型自动装配 no 不适用自动装配,必须通过 ref 元素定义


Spring AOP


实现核心业务系统服务代码之间的分开 通过一种特殊的技术手段来实现核心业务运行时能够实现系统服务的功能;aop 的本质是代理 通过对方法进行拦截、增强来实现的。


AOP 的基本概念


采用横向抽取机制,把分散在各个方法中的相同的代码抽取出来,然后在编译器或者是运行时再把这些代码应用到所需要执行的地方。


通知(Advice):aop 在切点上执行的增强处理。


通知的类型:


  • 前通知(methodBeforeAdvice):方法执行前做增强

  • 后通知(methodAfterAdvice):方法执行后做增强

  • 环绕通知(MethodInterceptor):方法执行前和后做增强

  • 返回通知(AfterReturningAdvice): 成功返回后 进行增强

  • 异常通知(ThrowsAdvice): 抛出异常后 进行通知


切点(Pointcut):就是带有通知的连接点,就是对那些类 哪些方法做增强。


切点的类型:


基于正则表达式 JdkRegexpMethodPointcut


基于 AspectJ 的表达式 AspectJExpressionPointcut


切面(Aspect):通常上就是一个类,里面定义了 通知切点


AOP = 通知 + 切点


AOP 案例 java 实现


// com.bean.User
复制代码


public class User {
复制代码


    public String login(String name){
复制代码


        return "name is "+ name ;
复制代码


    }
复制代码


    public String login(String name , int pwd){
复制代码


        return "name is "+name+", pwd is "+pwd ;
复制代码


    }
复制代码


}
复制代码


复制代码


// 新建一个环绕通知
复制代码


public class MyAroundAdivice implements MethodInterceptor{
复制代码


    @Override
复制代码


    public Object invoke(MethodInvocation arg0) throws Throwable{
复制代码


        System.err.println("方法执行前:"+arg0.getMethod().getName()+","+arg0.getArguments()[0]);
复制代码


        Object obj = arg0.proceed();
复制代码


        System.err.println("执行完成后...");
复制代码


        return obj;
复制代码


    }
复制代码


}
复制代码


复制代码


// 新建基于AspectJ切点的测试类
复制代码


@Test
复制代码


public void test(){
复制代码


    // 1. 声明通知
复制代码


    Advice advice = new MyAroundAdivice();
复制代码


    //2、基于AspectJ声明切点对象
复制代码


    AspectJExpressionPointcut cut = new AspectJExpressionPointcut();
复制代码


    //3、设置表达式
复制代码


    /*返回类型为String类型 com.bean.User的login方法  参数为String类型*/
复制代码


    //cut.setExpression("execution (String com.bean.User.login(String))");
复制代码


    //任意放回类型  com包下包括com子包下 任意方法 任意的参数0~n
复制代码


    cut.setExpression("execution(* com..*(..))");
复制代码


    //4、切点+通知 =AOP
复制代码


    Advisor advisor = new DefaultPointcutAdvisor(cut, advice);
复制代码


    //5、声明代理bean
复制代码


    ProxyFactory proxy = new ProxyFactory(new User());
复制代码


    //6、设置aop
复制代码


    proxy.addAdvisor(advisor);
复制代码


    //7、从代理中获取代理的对象
复制代码


    User user = (User) proxy.getProxy();
复制代码


    user.login("rose");
复制代码


    System.err.println("---------------");
复制代码


    user.login("jack",123);
复制代码


}
复制代码

AspectJ 语法


这个目录下的,或是类上的所有…任意的多个。0~N 个 execution ( com.bean.User.login(String,int))对 login 方法,必须要接收两个参数,且参数的类型必须是 Strig,int 的,且返回值无限制。且这个方法必须是 User 这个类的 Execution (* com..(…))返回所有的类型 所有在 com 包下的类,不包含子包 类的所有方法 接收任意多个参数 0~NExecution (* com….(…))在 com 包下的,包含子包下的所有类的所有方法,所有参数都切入 execution(* com….(String)) || execution(* com….(*,int))|| 逻辑或


AOP 案例 基于 XML 声明式 AspectJ


<bean id = "user" class = "com.bean.User">
复制代码


<!-- 定义一个切面 -->
复制代码


    <bean id="myBeforeAdvice" class="com.demo03.MyAdvice"></bean>
复制代码


    <aop:config>
复制代码


        <!-- 配置切入点 -->
复制代码


        <!-- 表达式(用来表示方法) -->
复制代码


        <!-- execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>),返回值,方法名,参数不能少 -->
复制代码


        <!-- *代表:任意值 方法名:全类名.方法名 参数中的..:任意个数,任意类型 -->
复制代码


        <aop:pointcut expression="execution(* com..*(..))" id="myPointcut" />
复制代码


        <!-- 切面配置 -->
复制代码


        <aop:aspect ref="myBeforeAdvice">
复制代码


            <!-- 配置前通知 -->
复制代码


            <aop:before method="doBefore" pointcut-ref="myPointcut" />
复制代码


            <!-- 配置后通知 -->
复制代码


            <aop:after method="doAfter" pointcut-ref="myPointcut" />
复制代码


            <!-- 配置返回通知 -->
复制代码


            <aop:after-returning method="doReturnAfter"
复制代码


                pointcut-ref="myPointcut" />
复制代码


            <!-- 配置环绕通知 -->
复制代码


            <aop:around method="doAround" pointcut-ref="myPointcut" />
复制代码


            <!-- 异常通知 -->
复制代码


            <aop:after-throwing method="doThrowing"
复制代码


                pointcut-ref="myPointcut" throwing="e" />
复制代码


        </aop:aspect>
复制代码


    </aop:config>
复制代码


</bean>
复制代码

基于注解的声明式 AspectJ (常用)


注解功能 @Aspect 注解在类上,声明这是一个切面 @Pointcut 注解在方法上声明是一个切点,且要声明 aspectj 的切点表达式 @Before 前通知 @After @AfterRetuning –正确执行成功返回值 @AfterThrow – 错误的执行,抛出异常后通知 @Around 环绕通知


第一步:新建注解的切面类


@Aspect
复制代码


@Component
复制代码


public class MyAspect {
复制代码


    //定义一个切入点表达式  使用一个返回值为void,方法体为空的方法来命名切点
复制代码


    @Pointcut("execution(* com..*(..))")
复制代码


    public void myPointCut(){}
复制代码


    
复制代码


    //前置通知 
复制代码


    @Before("myPointCut()")
复制代码


    public void doBefore(JoinPoint joinPoint)
复制代码


    {
复制代码


        System.out.println("前置通知,方法开始..."+":目标类是"+joinPoint.getTarget()+",被植入的增强方法是:"+joinPoint.getSignature().getName());
复制代码


    }
复制代码


    //后置通知
复制代码


    @After("myPointCut()")
复制代码


    public void doAfter()
复制代码


    {
复制代码


        System.out.println("后置通知,方法结束...");
复制代码


    }
复制代码


    //返回通知
复制代码


    @AfterReturning("myPointCut()")
复制代码


    public void doReturnAfter()
复制代码


    {
复制代码


        System.out.println("返回通知,方法最终结束...");
复制代码


    }
复制代码


    /**
复制代码


     * 环绕通知  ProceedingJoinPoint用来调用被增强的方法 
复制代码


     * 必须是Object的返回类型
复制代码


     * 必须接受一个参数,类型为ProceedingJoinPoint
复制代码


     * 必须throws Throwable
复制代码


     */
复制代码


    @Around("myPointCut()")
复制代码


    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable
复制代码


    {
复制代码


        System.out.println("环绕通知:begin...");
复制代码


         //执行被增强的方法
复制代码


        Object obj = joinPoint.proceed();
复制代码


        System.out.println("环绕通知:end.....");
复制代码


        return obj;
复制代码


    }
复制代码


    @AfterThrowing(value="myPointCut()",throwing="e")
复制代码


    public void doThrowing(Throwable e){
复制代码


        System.out.println("异常通知......."+e.getMessage());
复制代码


    }
复制代码


}
复制代码

第二步:xml 配置文件


<bean id="user" class="com.bean.User"></bean>
复制代码


<context:component-scan base-package="com.xxx"></context:component-scan>
复制代码


    <!-- 启动基于注解的声明式AspectJ支持 -->
复制代码


<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
复制代码

第三步:测试方法


@Test
复制代码


public void test1() {
复制代码


    ApplicationContext ctx = new ClassPathXmlApplicationContext("com/xxx/applicationContext.xml");
复制代码


    User user = ctx.getBean("user",User.class);
复制代码


    user.say();
复制代码


    //user.run();
复制代码


}
复制代码

如有问题 请指出


发布于: 2020 年 12 月 05 日阅读数: 31
用户头像

小Q

关注

还未添加个人签名 2020.06.30 加入

小Q 公众号:Java架构师联盟 作者多年从事一线互联网Java开发的学习历程技术汇总,旨在为大家提供一个清晰详细的学习教程,侧重点更倾向编写Java核心内容。如果能为您提供帮助,请给予支持(关注、点赞、分享)!

评论

发布
暂无评论
喝完可乐桶后程序员回归本源,开源Spring基础内容