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" />

 

posted @ 2020-10-24 12:16  大明湖畔的闰土  阅读(123)  评论(0编辑  收藏  举报