bean装配

  • Spring可以管理bean,也即是免new直接实例化一个对象,也可以将对象注入到其他类中。默认单例模式管理。
  • 步骤:定义一个实体类,bean.xml注册bean,在测试类获取该bean并使用它的成员方法。
  • bean.xml注册bean:

    <!-- 注册bean -->
    <bean id="permissionDao" 
        class="com.haien.shirochapter12.dao.impl.PermissionDaoImpl"/>
    
    <!-- Service:注入到其他bean中 -->
    <bean id="permissionService"                 
          class="com.haien.shirochapter12.service.impl.PermissionServiceImpl">
        <property name="permissionDao" ref="permissionDao"/>
    </bean>
    
  • 其中,属性id和name一样,都是指定bean的名称,只是id要求唯一(推荐),而name则可以重复,当获取bean时,会返回名称相同的第一个;当id和name都缺失时,bean名为class类全名。
  • 则PermissionServiceImpl必须有PermissionDaoImpl的setter,否则报错
public class PermissionServiceImpl implements PermissionService {
    public void setPermissionDao(PermissionDao permissionDao){
        this.permissionDao=permissionDao;
    }
}
  • 代码实例:ideaProjects/shirochapter12/resources/spring-beans.xml、service/impl/PermissionServiceImpl
  • 代码实例:Spring1ManageBean

IOC入门

  • 控制权反转:解耦,改内部类为参数传入形式,用set(接口)的方式来使方法调用者能动态指定。
  • 代码例子:有一个任务JavaWork,它的主要工作就是测试,所以它的主要方法就是doTest()。我们可以在这个方法中实例化一个测试员Tester的对象,并调用它的test()方法进行测试,但是这样我们要换人就留必须修改实例化的代码,所以我们决定让doTest()把控制权转交到JavaWork的调用者手上。我们在JavaWork中写一个setTester的方法,让JavaWork的调用者通过这个方法来指定测试员。那么这些测试员就都要实现一个相同的含有test()的接口,所以setTester()的入参类型就是这个接口了。
  • 代码实例:Spring2IOCFirst

    IOC详解

  • 依赖注入:也就是怎么把属性为其他实体类的类注册成bean
  • 代码实例:Spring3BeanZhuRu(普通属性)、Spring4BeanZhuRu2(含类属性)、Spring5BeanAutoWire(指定by某种方式注入)
  • 方法注入:配合scope=prototype实现动态注入
  • 代码实例:Spring5FunctionAutoWire

bean之间的关系

  • 继承、依赖、引用
  • 代码实例:Spring6RelationOfBeans

bean作用范围

  • 重点是singleton(默认)和prototype。singleton是指整个IOC容器中就只有一个bean,就算再申请一个也只是把原来的那个返回,SpringBoot中用注解注入的bean也是这样;prototype则每申请一次就返回一个新的bean
  • 切换bean作用范围参见笔记:Shiro第十六章-综合实例
  • 代码实例:Spring5FunctionAutoWire

FactoryBean

  • 在spring中可以利用FactoryBean来产生一些自定义配置的bean。

AOP

简介

  • 面向切面编程,是软件开发中的一个热点。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低。
  • 主要功能:日志记录、性能设计、安全控制、事务处理、异常处理等等。
  • 举个例子,旧式的日志管理是这样的:

    public class StudentServiceImpl implements StudentService{
    @Override
    public void addStudent(String name) {
        // 日志记录
        System.out.println("开始添加学生"+name);
        System.out.println("添加学生"+name);
        // 日志记录
        System.out.println("完成学生"+name+"的添加");
        /*这种日志记录方式入侵了逻辑代码,增加了代码的耦合度,完全不一样作用的代码也写到了一起*/
    }
    }
    
  • 代码实例:Spring7AOPFirst

详解

各种通知对逻辑代码的切入
  • 前置通知:方法执行前切入
  • 后置通知
  • 环绕通知(前置+后置)
  • 异常通知
代码实现
  1. 编写逻辑代码

    public class StudentServiceImpl implements StudentService{
    
        @Override
        public void addStudent(String name) {
    
            System.out.println("添加学生"+name);
    
        }
    }
    
  2. 定义通知切面类

     /*
     * 通知切面类
     */
     public class StudentServiceAspect {
         //前置通知
         public void doBefore(JoinPoint jp) { 
             System.out.println("类名:"+jp.getTarget().getClass().getName()); //jp可以获取类名,这里应该是StudentServiceImpl
             System.out.println("方法名:"+jp.getSignature().getName()); //addStudent
             System.out.println("开始添加学生:"+jp.getArgs()[0]); //获取参数,返回数组,这里只有一个参数,所以直接拿出第0个
         }
         //后置通知
         public void doAfter(JoinPoint jp) { 
             System.out.println("类名:"+jp.getTarget().getClass().getName()); //jp可以获取类名,这里应该是StudentServiceImpl
             System.out.println("方法名:"+jp.getSignature().getName()); //addStudent
             System.out.println("学生添加完成:"+jp.getArgs()[0]); //获取参数,返回数组,这里只有一个参数,所以直接拿出第0个
         }
    }
    

    3.配置xml文件

  • 多加三行

    <?xml version="1.0" encoding="UTF-8"?>
    <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">
    <!-- 使用切面应该多加上面三行带“aop”的东西 -->
    
  1. 装配bean(逻辑代码+切面)

    <bean id="studentServiceAspect" class="com.java1234.advice.StudentServiceAspect"></bean>
    
    <bean id="studentService" class="com.java1234.service.impl.StudentServiceImpl"></bean>
    
  2. 配置切面

    <aop:config>
        <!-- 定义切面 -->
        <aop:aspect id="studentServiceAspect" ref="studentServiceAspect">
            <!-- 定义切点 -->
            <!-- execution表示执行,括号内写方法,*表示任意(第一个表示任意返回值,
            第二个表示以前面为前缀的任意包,第三个表示包下任意方法;这样就能匹配
            service接口和实现中的任意方法了),..表示方法接受任意(数目、类型)参数 -->
            <!-- 定义通知 -->
            <aop:pointcut expression="execution(* com.java1234.service.*.*(..))" 
                 id="businessService"/>
            <aop:before method="doBefore" pointcut-ref="businessService"/>
            <!-- 执行切点businessService之前执行doBefore方法 (执行“添加学生”之前先打印“开始添加学生”)-->
            <aop:after method="doAfter" pointcut-ref="businessService"/>
        </aop:aspect> 
    </aop:config>
    
  • 匹配规则:
    • ()匹配了一个不接受任何参数的方法,(..)匹配一个接受任何数量、类型参数的方法,()匹配了一个接受任何类型参数的方法,(,String)则第一个参数可以是任何类型,但第二个必须是String。
    • 任意公共方法:execution(public (..))
    • 任意以“set”开头的方法:execution( set(..))
    • AccountService接口中的任意方法:execution( com.xyz.service.AccountService.(..))
    • service包的任意方法:execution( com.xyz.service..*(..))
    • service包及其子包的任意方法:execution( com.xyz.service..\.*(..)) //..匹配任何数量子包,包括零个
    • +:匹配指定类型的子类型:execution( com.haien.chapter16..service..\+.*(..))
    • java.*.String: 匹配java包下任意一级子包下的String类,如匹配java.lang.String,但不匹配java.lang.ss.String。
    • java..* :匹配java包及任何子包下的任何类型;如匹配java.lang.String、java.lang.annotation.Annotation
    • java.lang.Number+:匹配java.lang包下的任何Number的子类型;如匹配java.lang.Integer,也匹配java.math.BigInteger
  • 参考文章
  • 更详细的通配符说明
  1. 测试执行
public class T {

    private ApplicationContext ac;

    public static void main(String[] args) {
        ApplicationContext aContext=new ClassPathXmlApplicationContext("beans.xml");
        StudentService studentService=(StudentService)aContext.getBean("studentService");
        studentService.addStudent("张三");
    }
}
  • 测试结果:开始添加学生:张三
  • 添加学生张三
  • 学生添加完成:张三
  • 其他通知参见代码实例:Spring8AOPDetails

jdbc支持

  • Spring使用JdbcTemplate封装了jdbc的配置
  • 代码实例:Spring9JdbcTemplate(实现对学生的增删查改)
  • JdbcDaoSupport的使用:能使JdbcTemplate的使用简单一点点
  • 代码实例:Spring9JdbcDaoSupport
  • NamedParameterJdbcTemplate的使用:可以替代JdbcTemplate,支持命名的参数变量,具体体现在用问号蹲坑的形式被替换为用命名参数蹲坑,代码可读性up。
  • 代码实例:Spring10NamedParameterJdbcTemplate(主要是把Spring9JdbcTemplate的beans.xml和StudentDaoServiceImpl.java的JdbcTemplate类替换为NamedParameterJdbcTemplate类)

Spring对事务的支持

编程式事务管理:业务代码受到入侵

public void transferAccounts(final int count, final int userIdA, final int userIdB) { //因为内部类问题要加final
    //编程式事务管理,缺点:乱七八糟的代码侵入到业务逻辑里面了,这里的业务逻辑就两个方法而已
    transactionTemplate.execute(new TransactionCallbackWithoutResult() { //new一个内部类
        @Override
        protected void doInTransactionWithoutResult(TransactionStatus arg0) {
            //把方法加进来这两个方法就在一个事务中了
            bankDao.outMoney(count, userIdA);
            bankDao.inMoney(count, userIdB);
            }
        });
}
  • 代码实例:Spring11BianchengTransaction(转账的例子)

使用xml配置声明式事务管理(常用)

  • 在xml中配置事务管理即可,无需在业务代码中穿插任何其他代码
  • 首先是要引入命名空间

    <?xml version="1.0" encoding="UTF-8"?>
    <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"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    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
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!-- 使用Spring的事务管理应该多加上面三行带“tx”的东西,aop的也不能少 -->
    
  • 然后配置事务管理

    <!-- jdbc事务管理器 -->
    <bean id="transactionManager" 
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <!-- 配置事务通知 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!-- 暂定事务对所有方法都切入 -->
            <tx:method name="*" />
            <!-- 不过常见的配置应该像下面这样 -->
            <!-- 以下配置是有优先级的,假如前面方法名都扫描不到,
            那么应用最后的*,也即是扫描任意方法 -->  
        <tx:method name="insert*" propagation="REQUIRED" /> 
        <!-- propagation传播方式为required,当没有事务时自动创建事务
        (最常见的传播方式配置) -->
        <tx:method name="update*" propagation="REQUIRED" />  
        <tx:method name="edit*" propagation="REQUIRED" />  
        <tx:method name="save*" propagation="REQUIRED" />  
        <tx:method name="add*" propagation="REQUIRED" />  
        <tx:method name="new*" propagation="REQUIRED" />  
        <tx:method name="set*" propagation="REQUIRED" />  
        <tx:method name="remove*" propagation="REQUIRED" />  
        <tx:method name="delete*" propagation="REQUIRED" />  
        <tx:method name="change*" propagation="REQUIRED" />  
        <tx:method name="get*" propagation="REQUIRED" read-only="true" /> 
        <!-- 只是查询数据库的方法则设为只读 -->
        <tx:method name="find*" propagation="REQUIRED" read-only="true" />  
        <tx:method name="load*" propagation="REQUIRED" read-only="true" />  
        <tx:method name="*" propagation="REQUIRED" read-only="true" />  
        </tx:attributes>
    </tx:advice>
    
    <!-- 配置事务切面 -->
    <aop:config>
        <!-- 配置切点 -->
        <aop:pointcut id="serviceMethod" expression="execution(* com.java1234.service.*.*(..))" />
        <!-- 配置事务通知 -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod"/>
    </aop:config>
    
  • 逻辑代码清清爽爽,事务自动切入
public void transferAccounts(int count, int userIdA, int userIdB) {
    bankDao.outMoney(count, userIdA);
    bankDao.inMoney(count, userIdB);
}
  • 代码实例:Spring12StatementTransaction

使用注解配置声明式事务管理

  • 引入tx的命名空间,xml配置如下

    <!-- jdbc事务管理器 -->
    <bean id="transactionManager" 
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <tx:annotation-driven transaction-manager="transactionManager" />
    
    <!--不用配置事务切面了-->
    
  • 在service实现层添加@Transactional注解
@Transactional
public class BankServiceImpl implements BankService{

    private BankDao bankDao;

    public void setBankDao(BankDao bankDao) {
        this.bankDao=bankDao;
    }

    @Override
    public void transferAccounts(int count, int userIdA, int userIdB) {
        bankDao.outMoney(count, userIdA);
        bankDao.inMoney(count, userIdB);
    }

}
  • 代码实例:Spring12AnnotationTransaction

Spring整合Struts2和hibernate

  • 项目中使用了泛型接口:dao层接口使用泛型,可以实现对指定的不同实体进行数据库操作,而不用一个实体写一个接口处理。

spring必要maven依赖

  • spring核心依赖
    • spring-core:基本框架核心工具类,其他spring组件都需要依赖这个包(但好像不加也可以)
    • spring-beans:包含配置文件,创建和管理bean
    • spring-context
  • spring dao依赖(提供jdbcTemplate):spring-jdbc、spring-tx
  • spring web依赖:spring-web(好像不加也可以)、spring-webmvc