一、Spring框架搭建与使用

1. 官网上下载 jar 包,就是速度有些慢。 2. 导包:只需要导入五个 spring-frameword 中的 jar 包和一个依赖包,分别是
spring-aop-5.1.2.RELEASE.jar
spring-beans-5.1.2.RELEASE.jar
spring-context-5.1.2.RELEASE.jar
spring-core-5.1.2.RELEASE.jar
spring-expression-5.1.2.RELEASE.jar

com.springsource.org.apache.commons.logging-1.1.1.jar

3. 创建 Spring 框架的主配置文件,一般命名为 applicationContext.xml,一般放置在 src 目录下,如果使用的是 IDEA 开发环境,则可以直接创建出一个 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>

4. 配置 bean ,这一步骤是为了配置 Spring 框架的 IOC 和 DI,在配置文件中通过 bean 标签配置每一个类,配置完后 Spring 框架会自动创建类的对象,并存放在 Spring 容器中。

<bean name="user" class="com.gec.domain.User"></bean>

5. 在测试方法中
使用如下方法加载 Spring 配置文件,获取到 Spring 容器。

ApplicationContext ac = new ClassPathXMLApplicationContext("applicationContext.xml");

6. 从 Spring 容器中获取到对象
通过如下方法获取到指定对象,例如

User user = ac.getBean("user", User.class);

二、IOC(控制反转)

IOC 是 Spring 框架中非常重要的概念之一,它想表达的意思是,将对象创建的控制权交给 Spring 框架管理,Spring 会自动为我们创建对象,并把对象放置在 Spring 容器中,当我们需要创建对象时,可直接到容器中获取到对象。

  • XML 方式实现 IOC
  1. 在 Spring 配置文件中使用 bean 标签配置每一个类;
  2. 在测试方法中读取配置文件,Spring 会自动为配置的类创建对象并存储到 Spring 容器中
    总结:需要为每一个类都使用 bean 标签配置一次,过于麻烦。
<bean name="user" class="com.gec.domain.User" scope="prototype" factory-method="createUser">
	</bean>
  1. bean 标签中常用的属性:
    id:给 bean 设置唯一标识
    name:给 bean 设置名称;
    class:类的全路径名称;
    scope:设置单例还是多例,若值为 singleton 则表示单例;若值为 prototype 则表示多例。
    factory-bean:选择创建对象时使用的 bean 对象,一般配合 factory-method 一起使用,表示使用指定 bean 中的方法创建对象。
    factory-method:选择创建对象时调用的方法,若没有设置该属性,则默认是调用无参构造方法创建对象。
  • 注解方式实现 IOC
  1. 在类上添加注解 @Component ,相当于是配置 bean 。另外,由 @Component 衍生的三个作用相同的注解,分别是 @Controller、@Service、@Repository,主要是用在不同包下时加以区分。还可以像 XML 方式那样指定单例还是多例。
@Component
	@Scope(scopeName = "prototype")
	public class User {
	...
	}
  1. 在 Spring 配置文件中配置注解扫描。仅仅在类上添加注解还不够,还得通知 Spring 框架使用了注解配置,使用标签 context:component-scan 开启扫描,扫描指定包下的注解。另外需要注意的是,开启注解的标签需要配置了头部信息才能使用,需要添加 context 信息。
<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

<!--    开启扫描,-->
    <context:component-scan base-package="com.gec"/>
</beans>

三、DI(依赖注入)

DI 属于 IOC 内的内容,在实现 DI 前必须先实现 IOC。
DI 表达的意思是,在对象创建的过程中,将对象依赖的属性通过配置设置值给该对象。

  • XML 方式实现 DI:在 bean 标签中配置。
    可以有四种注入方式,分别为 set方法注入、构造方法注入、p命名空间注入和 spel表达式注入,主要使用前两种方式。
  1. set 方法注入:通过子标签 property 给类的属性赋值,类必须有 set 方法。
<bean name="user" class="com.gec.domain.User">
        <property name="username" value="张三"/>
    </bean>
  1. 构造方法注入:在类中重载构造方法,通过子标签 constructor-arg 给构造方法的参数传值。
<bean name="user" class="com.gec.domain.User">
<!--        注入方式:构造方法注入
                1. 使用标签 constructor-arg 添加构造函数参数
                2. index 属性设置参数的先后顺序,在有多个构造方法时可以使用
                3. type 属性设置参数的类型,在有相同参数名但不同类型时 可以使用该属性加一区分
            -->
        <constructor-arg name="username" value="1" index="2"/>
        <constructor-arg name="password" value="1234" type="int" index="0"/>
        <constructor-arg name="girl" ref="girl" index="1"/>
    </bean>
  1. p 命名空间方式注入:首先要在xml 头部添加 p 标签信息,然后才能在 property 标签中以 p:属性名 = “属性值” 方式注入值。
<bean name="user" class="com.gec.domain.User" p:username="p命名空间" p:password="12" p:girl-ref="girl">
    </bean>
  1. spel 表达式注入:在 property 标签的 value 属性中,使用 ‘#’ 添加字符串类型的值,其他基本数据类型或引用类型则与 set 方式一样。
<bean name="user" class="com.gec.domain.User">
        <property name="username" value="#{'宋江'}"/>
    </bean>
  • 注解方式实现 DI:在使用了注解 @Component 的类中
  1. @Value :若是基本数据类型 + String 类型,则可以使用该注解注入值;
  2. @Autowired :引用数据类型使用该注解注入,前提是该引用类型也配置了 bean。
  3. @Qualifier :当有多个相同类的对象时,可使用该注解加以区分。
  4. @Resource :是 Java 的注解,它的作用相当于是 @Autowired + @Qualifier,相当于简写版。
@Component
	@Scope(scopeName = "prototype")
	public class User {
	    @Value("王五")
	    private String username;
	
	    @Autowired
	    private Girl girl;
	    ...
	}
  • 在 XML 中注入复杂数据类型(数组、List、Map):在 property 标签内配置,不建议直接使用属性 value 设置值,应使用相应的子标签注入。
  1. 数组:使用 array 标签,在子标签 value 中注入基本数据类型或 String 类型,在子标签 ref 中使用属性 bean 注入引用类型。
<property name="array">
            <array>
                <value>12</value>
                <ref bean="girl"></ref>
            </array>
        </property>
  1. List 集合 :使用 List 标签,在子标签 value 中注入基本数据类型或 String 类型,在子标签 ref 中使用属性 bean 注入引用类型。
<property name="list">
            <list>
                <value>12</value>
                <ref bean="girl"></ref>
            </list>
        </property>
  1. map 集合 :使用 map 标签,子标签 entry 设置每一个键值对,属性 key、value 对应键值对。
<property name="map">
            <map>
                <entry key="name" value="大湖"/>
                <entry key="mygirl" value-ref="girl"/>
            </map>
        </property>
  1. properties:…

四、AOP(面向切面编程)

  • AOP 采用横向抽取机制,将涉及多业务流程的通用功能抽取出来并单独封装,形成独立的切面,在合适的时机将这些切面横向切入到业务流程指定的位置中。
  • AOP 的经典应用中就有:事务管理。
  • AOP 中的专业术语:
  1. Target(目标类):需要被代理的类;
  2. joinPoint(连接点):可能被 Spring 拦截的点(方法);
  3. pointCut(切入点):已经被增强的连接点(方法);
  4. advice(通知/增强):增强的代码,通用功能;
  5. weaving(织入):是指把增强的 advice 应用到目标对象 target 来创建代理类 proxy 的过程;
  6. AspectJ(切面):切入点 pointCut 和 通知 Advice 的结合。
  7. proxy(代理类).
  • XML 方式实现 AOP 的事务管理
  1. 导包:除了 4 个基本的 spring-frameword 的jar 包和 logging 包外,还需要4 个jar包,分别是
    spring-aop-5.2.0.RELEASE.jar
    spring-aspects-5.2.0.RELEASE.jar
    com.springsource.org.aopalliance-1.0.0.jar
    com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
  2. 在 Spring 配置文件的头部信息中,添加 aop 的信息。
  3. 准备目标类和通知类,并在 xml 中配置 bean 信息。
  4. 在 xml 中使用 aop:config 标签配置 AOP 信息。
<aop:config>
<!--        execution:执行方法,表达式简化
                1. 表达式中的访问修饰符可以去掉。
                2. 返回值类型可以为多种类型,设置为 *
                3. 执行方法名以及所属类名的前缀都可以是多样的。
                4. 访问当前包以及子包 ".."
-->
        <aop:pointcut id="pc" expression="execution(* com.gec.service..*Service.*User(..))"/>
        <aop:aspect ref="myAdvice">
<!--            <aop:before method="before" pointcut-ref="pc"></aop:before>-->
<!--            <aop:after method="after" pointcut-ref="pc"></aop:after>-->
<!--            <aop:after-returning method="after_returning" pointcut-ref="pc"></aop:after-returning>-->
<!--            <aop:after-throwing method="after_throwing" pointcut-ref="pc"></aop:after-throwing>-->
            <aop:around method="around" pointcut-ref="pc"></aop:around>
        </aop:aspect>
    </aop:config>
  1. 五种事务通知:
    before:前置通知,在方法执行前被调用;
    after-returning:后置通知,若执行方法没有抛出异常,则在方法执行后被调用,否则,抛出异常则不调用;
    after:后置通知(最终通知):不管执行方法是否抛出异常,都会在方法执行后被调用;
    after-throwing:异常通知,在方法抛出异常时被调用,可在该异常通知方法中设置异常处理;
    around:环绕通知,相当于 before 和 after 的结合体,该环绕通知方法要设置参数 ProceedingJoinPont,用于执行完前置通知内容后调用执行方法,并接收执行方法的返回值,在执行完后置通知内容后,再将返回值返回给上层调用,所以该环绕通知方法还需要将返回值类型设置为 Object 类型。
  • 注解方式实现 AOP 的事务管理
  1. 使用注解的方式还需要在 xml 的头部信息中添加 context 信息。使用标签 context:component-scan 扫描指定包下的注解信息。
  2. 配置 AOP 注解扫描,通知 Spring 框架 AOP 事务管理使用了注解的方式。使用标签 aop:aspectj-autoproxy 配置。
<!--    配置 AOP 注解扫描
        此处开关必须打开
-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
  1. 在目标类和通知类中都是用注解 @Component 将类交给 Spring 框架创建实例对象。
  2. 在通知类中还需要添加注解 @Aspect 表示该类是一个切面,另外在相应的通知方法中,使用相应的注解 @Before/@AfterReturning/@After/@AfterThrowing/@Around 设置方法为相应的事务通知方法。
@Component(value = "myAdvice")
	@Aspect
	public class MyAdvice {
	    @Before(value = "execution(* com.gec.service..*Service.*User())")
	    void before(){
	        System.out.println("开启事务");
	    }
	
	    @After(value = "execution(* com.gec.service..*Service.*User())")
	    void after(){
	        System.out.println("提交事务");
	    }
	
	    @AfterReturning(value = "execution(* com.gec.service..*Service.*User())")
	    void after_returning(){
	        System.out.println("后置通知,出现异常则不执行次方法");
	    }
	
	    @AfterThrowing(value = "execution(* com.gec.service..*Service.*User())")
	    void after_throwing(){
	        System.out.println("异常处理通知");
	    }
	
	    @Around(value = "execution(* com.gec.service..*Service.*User())")
	    Object around(ProceedingJoinPoint pj) throws Throwable {
	        //执行前置通知
	        System.out.println("前置通知");
	        //调用执行方法,获得执行方法的返回值
	        Object proceed = pj.proceed();
	        //执行后置通知
	        System.out.println("后置通知");
	        //后置通知结束后 需要把返回值返回给上层调用
	        return proceed;
	    }
	}

五、JdbcTemplate 模板类

  • JdbcTemplate 模板类是 Spring 为传统的 JDBC API 进行封装,简化持久层操作。
  • 使用 JdbcTemplate:
  1. 导包:除了四个基本的spring-frameword的jar包和logging包外,还需要
    spring-aop-5.2.0.RELEASE.jar
    spring-jdbc-5.2.0.RELEASE.jar
    spring-test-5.2.0.RELEASE.jar
    spring-tx-5.2.0.RELEASE.jar
    com.springsource.com.mchange.v2.c3p0-0.9.1.2.jar
    mysql-connector-java-8.0.11.jar
  2. 在 xml 中配置数据源。这里使用的是 C3P0 方式连接数据库,使用 bean 标签配置 ComboPooledDataSource 数据源,设置连接数据库的信息。
<!--    配置数据源-->
    <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"/>
        <property name="user" value="root"/>
        <property name="password" value="1234"/>
    </bean>
  1. 在 xml 中配置 JdbcTemplate,将数据源注入到 JdbcTemplate 中,让 Spring 框架自动为我们创建 JdbcTemplate 对象。
<!--    配置 JDBCTemplate-->
    <bean name="JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
  1. 在测试类中使用 JdbcTemplate 对象进行增删改查:
    增删改使用 update(String sql[,Object…arg])方法即可
    查询可分为三种情况
    a. 返回 List 集合,使用 query(String sql,RowMapper< T > rowMapper)方法,匿名实现RowMapper 接口,设置查询结果集的接收类型。
    b. 返回一个 Object 对象,使用 queryForObject(String sql,RowMapper< T > rowMapper,Object…arg),匿名实现 RowMapper 接口,设置查询结果集的接收类型,可变参数设置查询的条件。
    c. 返回一个整数,如计算总数等情况,使用 queryForObject(String sql,Class< T > requiredType),requiredType 设置返回类型,如可设置为 Integer.class 类型。
@Test
    public void fun(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

        JdbcTemplate jdbcTemplate = (JdbcTemplate) ac.getBean("JdbcTemplate");
        
		/*
          增删改使用 update方法
         */
        int i = template.update("insert into province values(null, '山东')");
        System.out.println(i);

        /*、
          查询使用 query、queryForObject等方法
         */
        //返回 list 集合
        List<Province> query = template.query("select * from province", new RowMapper<Province>() {
            @Override
            public Province mapRow(ResultSet resultSet, int i) throws SQLException {
                Province p = new Province();
                p.setPid(resultSet.getInt(1));
                p.setPname(resultSet.getString(2));
                return p;
            }
        });

        for (Province province : query) {
            System.out.println(province);
        }
        System.out.println("------------------------");
        //返回一个 Object 对象
        Province province = template.queryForObject("select * from province where pid = ?", new RowMapper<Province>() {
            @Override
            public Province mapRow(ResultSet resultSet, int i) throws SQLException {
                Province p = new Province();
                p.setPid(resultSet.getInt(1));
                p.setPname(resultSet.getString(2));
                return p;
            }
        }, 1);
        System.out.println(province);
        System.out.println("------------------------");

        //返回整型数据
        Integer count = template.queryForObject("select count(*) from province", Integer.class);
        System.out.println("总记录数:" + count);
    }