• Springday01-02


    Spring介绍

    Spring是一个企业级引用框架,其中包含了大量的各种应用软件。Spring框架为现代基于Java的企业应用程序提供了一个全面的编程和配置环境,能够在任何类型的部署平台上进行部署,其中核心是IoC和AOP。

    Spring中的两个核心概念

    IoC

    控制反转(Inversion of control),将原来由我们完成的实例化过程,交给容器来完成。将组建对象的控制权从代码本身转移到外部容器中。

    组件化的思想:分离关注点,使用接口,不再关注实现。

    • DI:依赖注入。依赖于某一种媒介完成对某一个对象的初始化或者是赋值。

    AOP

    面向切面编程

    Spring优点

    • 低侵入式的设计
    • 独立于各种应用服务器
    • 依赖注入将组件关系透明化,降低了耦合度
    • 面向切面编程特性允许将通用任务进行集中式处理
    • 与第三方框架的良好整合

    Spring工程构建

    Maven Spring依赖注入

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.6.RELEASE</version>
    </dependency>

    Spring核心配置文件编写

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans.xsd">
        
    </beans>

    完成控制反转和依赖注入

    <bean id="userInfo" class="com.domain.UserInfo">
            <!--完成依赖注入 DI-->
            <property name="username" value="张三" />
            <property name="content" value="刘家豪大笨蛋!" />
    </bean>
    • bean:
      • id:该bean对象名
      • class:指向对应的类路径
    • property:
      • name:对象内的某个属性名
      • value:属性值(基本数据类型使用value)
      • ref:属性值(引用数据类型使用ref引用对象)

    注意:在同一个配置文件下,bean中的id不能重复。

    测试:

    @Test
    public void test(){
       // 获取IoC容器的对象
      ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
      // 使用容器对象的getBean("id")来获取对应的bean对象
      UserInfo userInfo = (UserInfo)ac.getBean("userInfo") ; 
      // 使用bean对象进行操作  
      userInfo.print();
    }

    Spring中的IoC产生的对象是否是单例模式

    @Test
    public void test1(){
      ApplicationContext ac = new ClassPathXmlApplicationContext("applicationConetxt.xml");
      // 第一次向容器中获取userInfo的bean对象
      UserInfo userInfo = (UserInfo)ac.getBean("userInfo");
      userInfo.print();
       System.out.println(userInfo);
      // 第二次向容器中获取userInfo的bean对象
       UserInfo userInfo2 = (UserInfo)ac.getBean("userInfo");
      userInfo2.print();
      System.out.println(userInfo2);
    }    

    观察结果可以发现,userInfo和userInfo2是同一个对象,那么说明在调用同一个bean时,获取回来的对象都为同一对象。所以在Spring的设计中,所使用的是单例模式。每调用一个新的bean,都会创建一个新的对象。

    虽然都是对着同一个类进行实例化,但是每一个bean代表着一个对象。

    应用XML编程实现AOP

    面向切面编程(Aspect Oriented Programming)。其目的是为了拦截某些类下的某些方法、参数、返回值内容。在此基础上,通过增强形式,使用代理模式显示AOP编程。

    AOP的代理实现形式

    通过JDK动态代理实现

    JDK动态代理模式的实现,是要求必须拥有接口。Spring能够对任何一个对象都进行代理设计,此时如果某个对象没有对应的接口,那么JDK动态代理是无法创建对应的代理对象,这时Spring AOP就会使用CGLIB生成。

    通过CGLIB动态代理实现

    CGLIB是通过目标类实现的一种代理方式,因此不需要提前准备对应的接口。在Spring生成代理对象时,实际上是生成代理目标的一个子类。

    public class SampleClass {
        public void test(){
            System.out.println("hello world");
        }
     
        public static void main(String[] args) {
            Enhancer enhancer = new Enhancer();
            //告诉代理类,他的父类是谁
            enhancer.setSuperclass(SampleClass.class);
             //通过setCallback方法,拦截对应的调用方法进行增强
            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(obj, args);
                    System.out.println("after method run...");
                    return result;
                }
            });
            SampleClass sample = (SampleClass) enhancer.create();
            sample.test();
        }
    }

    通过编程形式基于XML实现AOP

    • 引入aspectweaving依赖
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.9.2</version>
            </dependency>
    • 修改配置文件

      

    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop.xsd">
    • 编写增强通知类
    public class LoggerService{
       /**
         * 前置通知
         * @param joinPoint 连接点
       */
      public void before(JoinPoint joinPoint){
        /**
           * joinPoint.getTarget():获取到目标的类
           * joinPoint.getSignature().getName():获取到方法
           * joinPoint.getArgs():获取到参数
        */
         System.out.println(joinPoint.getTarget());
      } 
       /**
         * 后置通知
         * @param joinPoint  连接点
         * @param result  返回值结果
       */
        public void afterReturning(JoinPoint joinPoint, Object result) {
            System.out.println("后置通知:" + joinPoint.getTarget() + "的" + joinPoint.getSignature().getName() + ",返回值是:" + result);
        }
        
    }    
    • 在核心文件中增加aop的配置
    <!--增强bean设置-->
    <bean id=""loggerService" class="com.csi.aop.LoggerService"/>
    <!--目标对象-->
    <bean id="XXX" class="pointcut切入点实现类"/>
    <!--aop配置-->
    <aop:config>
       <!--接入点-->
      <aop:pointcut id="pointcut" expression="execution(* com.csi.service..*.*(..))"/>
      <!--切面  织入过程-->
      <aop:aspect ref="loggerService">
        <!--前置增强-->
        <aop:before method="before" pointcut-ref="pointcut"/>
        <!--后置增强-->
        <aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/>        
      </aop:aspect>    
    </aop:config> 
    • 测试
    ApplicationContext ctx = new ClasspathXMLApplicationContext("applicationXXX.xml") ;
    Object obj = ctx.getBean("id") ;
    
    obj.xxx() ;
    • 结果

    前置通知:com.csi.service.impl.PersonServiceImpl@1dde4cb2的list,参数是[] 调用了list方法

    后置通知:com.csi.service.impl.PersonServiceImpl@1dde4cb2的list,返回值是:[com.csi.domain.UserInfo@72057ecf, com.csi.domain.UserInfo@1afd44cb]

    使用场景

    • 日志
      • 会降低日志输出灵活度,没有办法进行个性化的输出
    • 权限
      • 如果将权限控制下沉到service层,那么基恩上没有太大意义
    • 事务管理
      • 由于事务管理是一成不变的,所以是最适合的场景

    IoC和AOP使用扩展

    IoC构造注入

    使用内部bean

    <bean id="userService" class="com.csi.service.impl.UserServiceImpl">
            <!--引用数据类型,只能够通过ref或者是内部bean的方式进行赋值-->
            <constructor-arg name="userDao">
                <bean class="com.csi.dao.impl.UserDaoImpl" />
            </constructor-arg>
    </bean>

    使用ref引用形式

        <bean id="userDao" class="com.csi.dao.impl.UserDaoImpl"/>
    
        <bean id="userService" class="com.csi.service.impl.UserServiceImpl">
            <!--引用数据类型,只能够通过ref或者是内部bean的方式进行赋值-->
            <constructor-arg name="userDao" ref="userDao" />
        </bean>

    通过构造中的参数索引顺序赋值

    <bean id="userService" class="com.csi.service.impl.UserServiceImpl">        
            <constructor-arg index="0" ref="userDao" />
            <constructor-arg index="1" value="50" />
    </bean>

    通过type类型注入

    <bean id="userService" class="com.csi.service.impl.UserServiceImpl">
            <!--引用数据类型,只能够通过ref或者是内部bean的方式进行赋值-->
            <!--<constructor-arg name="userDao" ref="userDao" />-->
            <!--<constructor-arg index="0" ref="userDao" />
            <constructor-arg index="1" value="50" />-->
            <constructor-arg type="com.csi.dao.UserDao" ref="userDao" />
            <constructor-arg type="int" value="20" />
    </bean>

    日期格式注入

    <bean id="simpleDateFormat" class="java.text.SimpleDateFormat"/>
    
    <bean id="userInfo" class="com.csi.doamin.UserInfo">
        <constructor-arg name="content" value="hello word">
        <constructor-arg name="borndate">
            <bean factory-bean="simpleDateFormat" factory-method="parse">
                <constructor-arg value="2020-10-24">
            </bean>
        </constructor-arg>
    </bean>

    设值注入(setter)和构造注入的区别

    集合、数组、map、Properties类型注入

    <bean id="userInfo" class="com.csi.domain.UserInfo">
      <!--属性Properties注入-->
      <property name="properties">
        <props>
          <prop key="username">root</prop>
          <prop key="password">123456</prop>
        </props>
      </property>
    
      <!--map注入-->  
      <property name="map">
        <map>
           <entry key="key1" value="val1"/>
           <entry key="key2" value="val2">
        </map>
      </property>
    
      <!--list、array、set-->
      <property name="list">
        <array>
           <value>张三</value>
           <value>李四</value>
        </array>
      </property>
    </bean>   

    AOP扩展使用

    异常通知

     在出现异常时,Spring AOP会自动捕获异常,并使用代理对象处理

    /**
         * 异常通知
         */
        public void afterThrowing(JoinPoint joinPoint,RuntimeException e) {
            System.out.println(joinPoint.getTarget() + "的" + joinPoint.getSignature().getName() + ",报出的异常信息:" + e);
        }
    <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="e" />

    最终通知

        Spring AOP的最终通知是通过finally实现的,无论发生任何情况,都会执行。

    /**
         * 最终通知:无论什么情况,都会执行
         */
        public void after(JoinPoint joinPoint) {
            System.out.println("最终通知:" + joinPoint.getTarget() + "的" + joinPoint.getSignature().getName() + ",参数是" + Arrays.toString(joinPoint.getArgs()));
        }
    <aop:after method="after" pointcut-ref="pointcut" />

    环绕通知 

    环绕通知功能十分强大,包含了四种全部通知类型,主要是通过暴露的原生方法调用所执行的目标方法(类似于JDK动态代理的invoke方法),因此可以获取到原生方法的所有状态加以处理。

    /**
         * 环绕通知:在执行方法的前、后以及异常信息及finally中都可以灵活处理
         */
        public void afterAround(ProceedingJoinPoint pjp) {
            System.out.println("开启事务...");
            try {
                //调用代理目标的方法
                Object result = pjp.proceed() ;
                System.out.println("事务提交");
            } catch (Throwable throwable) {
                throwable.printStackTrace();
                System.out.println("事务回滚");
            } finally {
                System.out.println("释放SqlSession对象");
            }
        }
    <aop:around method="afterAround" pointcut-ref="pointcut" />
  • 相关阅读:
    软件构造期末复习考点总结
    设计模式
    怎么写测试策略
    FreeRTOS任务创建、启动调度器、任务切换的过程分析——基于ARM-CotexM3
    Altium Designer 创建集成封装库(.IntLib文件)的方法
    Altium Designer多通道设计的原理与应用实例
    我的编程命名风格
    计算机网络学习笔记
    基于串口通信做my_printf时遇到的坑儿
    git常用命令
  • 原文地址:https://www.cnblogs.com/heureuxl/p/13868367.html
Copyright © 2020-2023  润新知