Spring学习

something needed

  • JUnitspring-test 单元测试框架
  • Dbutils 数据库工具,QueryRunner类是DbUtils的核心类
  • spring-context spring容器
  • c3p0 JDBC连接池
  • aspectj 面向切片的框架
  • spring-tx 注解事务

概述

  1. 概述
    • Spring是分层的全栈轻量级开源框架,以IoC和AOP为内核,提供表现层Spring MVC和持久层Spring JDBC以及业务层事务管理等。
    • 两大核心:IoC和AOP。
      • IoC:控制反转,把创建对象的权利交给框架,削减耦合。
      • AOP:面向切片编程
    • 优势:方便解耦,简化开发;AOP编程的支持;声明式事务控制;方便测试;集成框架
  • 体系结构
  1. 程序的耦合及解耦
    • 耦合:程序间的依赖关系
    • 解耦:降低依赖关系。在实际开发中,应编译期不依赖,运行时才依赖。
      1. 通过读取配置文件来获取要创建的对象全限定类名
      2. 使用反射来创建对象Class.forName("com.mysql.jdbc.Driver"),而避免使用new关键字DriverManager.registerDriver(new com.mysql.jdbc.Driver)。其他类之间的关系也是一样

基于XML的IOC配置

  1. IOC概念:控制反转,把创建对象的权利交给框架,削减耦合。
  2. 基于XML
    • pom.xml
    • 在java文件夹下面新建dao service ui文件夹,dao是连接数据库的持久层,service是中间处理的逻辑层,ui为表现层
    • resources下新建bean.xml,通过这个 把对象创建交给spring来管理,根据标志获取对象
    • 在ui中创建一个类,获取spring的ioc容器,获取对象
      1. ApplicationContext:在构建核心容器时,创建对象采用立即加载,只要一读取完配置文件就马上创建配置文件中的配置对象
  3. spring对bean的管理细节
    • 创建bean的三种方式
      1. 使用默认构造函数创建。在配置文件中使用bean标签,配以id和class属性之后,且没有其他标签和属性,若类中没有默认构造函数 则创建失败
      2. 使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器),需要指定方法名,属性:id、factory-bean、factory-method
      3. 使用静态工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器),属性:id、class、factory-method
      4. 在使用外部jar包中的东西时,可能会用到第二三种方法
    • bean对象的作用范围,通过bean标签中的scope属性调整。
      • 取值:singleton单例、默认值;prototype多例;request作用于web应用的请求范围;session作用于web应用的会话范围;global-session全局会话
    • bean对象的生命周期
      1. 单例:容器创建时 对象出生,容器在 对象在,容器亡 对象亡。
      2. 多例:使用对象时创建;在使用过程中一直活着;当对象长时间没用,且没有其他机制引用时,由java垃圾回收机制销毁。
  4. 依赖注入:Dependency Injection
    • 依赖关系的维护称为依赖注入
    • 能注入的数据:基本类型和String;其他bean类型(在配置文件中或者注解配置过的bean);复杂类型、集合类型
    • 注入方式:
  5. 使用构造函数提供,使用标签constructor-arg,放在bean标签内部,其中的属性有:type 指定要注入的数据的类型;index 指定构造函数中指定索引位置;name 指定构造函数中的数据名称;value 提供数据;ref 指定出现过的bean对象
  1. // 类实现中
  2. private String name;
  3. private Integer age;
  4. private Date date;
  5. public AccountServiceImpl(String name, Integer age, Date date){
  6. this.name = name;
  7. this.age = age;
  8. this.date = date;
  9. }
  10. // xml中
  11. <bean id="accountService" class="com.spring1.service.impl.AccountServiceImpl">
  12. <constructor-arg name="name" value="月亮"></constructor-arg>
  13. <constructor-arg name="age" value="12"></constructor-arg>
  14. <constructor-arg name="date" ref="date"></constructor-arg>
  15. </bean>
  16. <bean id="date" class="java.util.Date"></bean>
  1. 使用set方法提供,使用标签property,放在bean标签内部,其中的属性有:name 指定注入时所调用的set方法名称;value 提供数据;ref 指定出现过的bean对象
  2. 使用注解提供。
    • 复杂类型的注入。数组、List、Set、Map、Properties。用于给List结构注入的标签有list、array、set;用于给Map结构集合注入的标签有map、props。总结:结构相同 标签可以互换

基于注解的IOC配置

  1. 常用ioc注解
    • 用于创建对象的注解,与在xml配置文件中编写一个<bean>标签实现的功能是一样的。
      1. @Component用于把当前类对象存入spring容器中,放在实现类前面,属性为value 用于指定bean的id(默认值为当前类名首字母改小写)[容器.getBean(bean的id)]。下面仨注解的作用和属性与这个一样,但可以使三层对象更加清晰。
      2. @Controller 表现层
      3. @Service 业务层
      4. @Repository 持久层
    • 用于注入数据的注解,其作用与<bean>标签中写一个<property>标签的作用一样
      1. @Autowired 自动按照类型注入,只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功,如果有多个类型匹配时 再匹配名称。可以放在变量前 也可以放在方法前
      2. @Qualifier 在按照类型注入的基础上再按照名称注入,在给类成员注入时不能单独使用(要与Autowired配合使用),但给方法参数注入时可以。属性:value 用于指定注入bean的id
      3. @Resource 直接按照bean的id注入,可以独立使用,属性 name指定bean的id
    • 以上3个注入都只能注入其他bean类型的数据,而不能注入基本类型和String类型,另外 集合类型只能通过XML来实现
      1. @Value 用户注入基本类型和String类型的数据,属性 value用于指定数据的值,可以用spring的SpEL(写法:${表达式}
    • 用于改变作用范围的注解,与使用scope属性的作用一样
      1. @Scope 指定bean的作用范围,属性value指定范围的取值,常用singleton prototype
    • 和生命周期相关的注解,与在<bean>使用init-methoddeatroy-method属性一样。
      1. @PreDestroy 销毁
      2. @PostConstruct 初始化
  2. 案例使用xml方式和注解方式实现单表的CRUD操作
  3. 改造基于注解的ioc案例 -> 纯注解
  • 新注解
    1. @Configuration,指定当前类是一个配置类
    2. @ComponentScan,用于 通过注解指定spring在创建容器时要扫描的包,相当于bean.xml中的context:component-scan
    3. @Bean,用于把当前方法的返回值作为bean对象存入spring的ioc容器中,属性name,指定bean的id,默认值为当前方法的名称
    4. @Import,用于导入其他的配置类
    5. @PropertySource,用于指定properties文件的位置,属性 value指定文件的名称和路径
  1. spring和Junit的整合
    把获取容器与获取对象的句子抽离出来,减少冗余
  • 导入spring整合junit的jar spring-test
  • 使用Junit提供的一个注解@RunWith,把原有的main方法替换了,替换成spring提供的
  • 告知spring的运行器,spring的ioc创建是基于xml还是注解的,并说明位置,使用注解@ContextConfiguration,属性 locations指定xml文件的位置,加上classpath关键字 表示在类路径下;classes指定注解类所在的位置

动态代理

  • 特点:字节码随用岁创建,随用随加载
  • 作用:不修改原码的基础上对方法增强
  • 分类:
    1. 基于接口的动态代理
      • 涉及的类:Proxy,使用Proxy类中的newProxyInstance方法创建代理对象,被代理类最少实现一个接口。
      • newProxyInstance方法的参数:
      • ClassLoader 类加载器,用于加载代理对象字节码,和被代理对象使用相同的类加载器。写法固定
      • Class[] 字节码数组,用于让代理对象和被代理对象有相同的方法。写法固定
      • InvocationHandler,用于提供增强的代码,让我们写如何代理,一般需要写一个该接口的实现类,通常是匿名内部类,但不是必须得。此接口的实现类都是谁用水写。
      • 做法:建立一个类factory.BeanFactory,用于创建Service的代理对象。减少了写事务相关的代码
    2. 基于子类的动态代理
      • 涉及的类:Enhancer,使用Enhancer中的create方法创建代理对象,被代理类不能是最终类。create中的参数:
      • Class 字节码,指定被代理对象的字节码。
      • Callback 用于提供增强的代码,一般写的都是该接口的子接口实现类 MethodInterceptor

AOP 面向切片编程

  • 把通用逻辑从业务逻辑中分离出来,降低耦合
  • 基本术语:
    • Join point: 拦截点,如某个业务方法。
    • Pointcut: 切入点,Joinpoint 的表达式,表示拦截哪些方法。一个 Pointcut 对应多个 Joinpoint。
    • Advice: 要切入的逻辑。
      ? BeforeAdvice 在方法前切入。
      ? After Advice 在方法后切入,抛出异常时也会切入。
      ? AfterReturningAdvice 在方法返回后切入,抛出异常则不会切入。
      ? AfterThrowingAdvice 在方法抛出异常时切入。
      ? Around Advice 在方法执行前后切入,可以中断或忽略原有流程的执行。
  • spring中基于xml的AOP配置步骤:

    1. 把通知Bean交给spring来管理
    2. 使用aop:config标签开始AOP配置
    3. 使用aop:aspect标签表明配置切面
      1. id属性:是给切面提供一个唯一标识
      2. ref属性:指定通知类bean的id
    4. aop:aspect标签的内部使用对应标签来配置通知的类型
      1. aop:before:表示配置前置通知(在切入点方法执行之前执行)。method属性指定Logger类中哪个方法是前置通知;pointcut属性 指定切入点表达式,该表达式的含义指的是对业务层中哪些方法增强
    5. 切入点表达式的写法:关键字execution,表达式:访问修饰符 返回值 包名.包名.包名….类名.方法名(参数列表)

      1. 全通配写法,访问修饰符可以省略: .. . (..)
      2. 实际开发中,切入点表达式的通常写法:切到业务层下的所有方法: com.spring5.service.impl. . *(..)
      3. 有多个通知时,可以把表达式抽离出来单独写,如:
        aop:pointcut可以放在aspect前面,这样就变成了所有切面可用

        1. <aop:before method="beforePrintLog" pointcut-ref="pt1"></aop:before>
        2. <aop:pointcut id="pt1" expression="execution(* com.spring5.service.*.*(..))"/>
      4. 环绕通知aop:around,根据位置不同而设置为不同的通知

  • 基于注解的AOP配置,使用注解的环绕比其他几个要好

    • @Aspect 表示当前类是一个切片类
    • 切入点表达式:

      1. @Pointcut("execution(* com.spring5.service.*.*(..))")
      2. private void pti(){ }
    • @Before("pti()")前置通知…

    • @Around("pti()")环绕通知,根据语句的位置不同而设置为不同的通知

JdbcTemplate

  1. spring的JdbcTemplate,操作关系型数据库
  2. 作用:用于和数据库交互的,实现对表的CRUD操作
  3. 如何创建该对象

    1. 先在bean.xml中配置,再获取容器、获取对象:
      1. ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
      2. JdbcTemplate jt = ac.getBean("jdbcTemplate",JdbcTemplate.class);
  4. 对象中的常用方法

    • 保存:jt.update("insert into account(name,money) value(?,?)","aho",123);
    • 更新:jt.update("update account set money=? where id=?",90,2);
    • 删除:jt.update("delete from account where id=?",3);
    • 查询所有:

      1. List<Account> accounts = jt.query("select * from account where money>?",new BeanPropertyRowMapper<Account>(Account.class),1000);
      2. for 输出
    • 查询一个:

      1. List<Account> accounts = jt.query("select * from account where id=?",new BeanPropertyRowMapper<Account>(Account.class),4);
      2. System.out.println(accounts.ifEmpty() ? "没有内容" : accounts.get(0));
    • 查询返回一行一列:

      1. Long count = jt.queryForObject("select count(*) from account where money>?",long.class,1000);

Spring事务控制

  1. 当一个方法中要与数据库进行多次操作(如 获取数据之后再存入数据),每次操作的获取一个单独的连接,从而当中间出现异常时,前面的连接正常执行 而之后的连接就无法进行。
  2. so需要使用ThreadLocal对象把Connection和当前线程绑定,从而使一个线程中只有一个能控制事务的对象

基于xml的声明式事务控制

  1. spring中基于xml的声明式事务控制的步骤:
    1. 配置事务管理器
    2. 配置事务的通知
      • 此时需要导入事务的约束、tx的命名空间和约束,也需要aop的
      • 使用tx:advice标签配置事务通知,属性:id 给事务通知起一个唯一标识;transaction-manager 给事务通知提供一个事务管理器引用
    3. 配置AOP中的通用切入点表达式
    4. 建立事务通知和切入点表达式的对应关系
    5. 配置事务的属性
      • 在事务的通知标签内部配置,有不少的参数,没写

基于注解的事务控制

  1. spring中基于注解的声明式事务控制配置步骤:
    1. 配置事务管理器
    2. 开启spring对注解事务的支持
    3. 在需要事务支持的地方使用@Transactional,其余都换成注解方式的数据注入