[Spring小结]注解和xm配置文件混合开发

  spring和Mybatis使用起来有区别,Mybatis纯xml配置文件开发是至少两个配置文件,一个主配置文件,一个dao配置文件。spring的纯xml开发只有一个配置文件,使用注解和xml混合开发和纯xm配置开发要实现的功能是一样的,只是配置的地方不一样。

1、IOC配置

1.1 创建maven工程

 projectName
	|-src
		|-main
			|-java
				|-cn.klb
					|-dao
						|-AccountDao
						|-impl
							|-AccountDaoImpl
					|-service
						|-AccountService
						|-impl
							|-AccountServiceImpl
				|-resource
					|-bean.xml

1.2 导入jar包坐标

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
    </dependencies>

1.3 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 告知 spring 创建容器时要扫描的包 -->
    <context:component-scan base-package="cn.klb"></context:component-scan>

</beans>

1.4 编写实体类

  持久层

package cn.klb.dao;

/**
 * @Author: Konglibin
 * @Description:
 * @Date: Create in 2020/3/8 22:28
 * @Modified By:
 */
public interface AccountDao {

    public void saveAccount();
}
package cn.klb.dao.impl;


import cn.klb.dao.AccountDao;
import org.springframework.stereotype.Repository;

/**
 * @Author: Konglibin
 * @Description:
 * @Date: Create in 2020/3/8 22:28
 * @Modified By:
 */
@Repository("accountDaoImpl")
public class AccountDaoImpl implements AccountDao {
    public void saveAccount() {
        System.out.println("AccountDaoImpl的saveAccount执行了...");
    }
}

  业务层:

package cn.klb.service;

/**
 * @Author: Konglibin
 * @Description:
 * @Date: Create in 2020/3/8 22:29
 * @Modified By:
 */
public interface AccountService {
    public void saveAccount();
}

package cn.klb.service.impl;

import cn.klb.dao.AccountDao;
import cn.klb.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @Author: Konglibin
 * @Description:
 * @Date: Create in 2020/3/8 22:29
 * @Modified By:
 */

@Service("accountServiceImpl")
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    public void saveAccount() {
        System.out.println("AccountServiceImpl的saveAccount执行了...");
        accountDao.saveAccount();

    }
}

1.4 测试代码

package cn.klb;

import cn.klb.service.AccountService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @Author: Konglibin
 * @Description:
 * @Date: Create in 2020/3/10 13:28
 * @Modified By:
 */
public class AnnotationTest {

    @Test
    public void testSpringIOC(){
        //1.使用 ApplicationContext 接口,就是在获取 spring 容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");

        //2.根据 bean 的 id 获取对象
        AccountService aService = (AccountService) ac.getBean("accountServiceImpl");
        aService.saveAccount();
    }
}

  输出结果:
在这里插入图片描述

1.5 小结

  IOC注解开发其实就是把<bean>标签的内容移到各个类上面,持久层用@Repository,业务层用@Service,其实这两个标签等同于@Component,源码如下:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {

	/**
	 * The value may indicate a suggestion for a logical component name,
	 * to be turned into a Spring bean in case of an autodetected component.
	 * @return the suggested component name, if any (or empty String otherwise)
	 */
	@AliasFor(annotation = Component.class)
	String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {

	/**
	 * The value may indicate a suggestion for a logical component name,
	 * to be turned into a Spring bean in case of an autodetected component.
	 * @return the suggested component name, if any (or empty String otherwise)
	 */
	@AliasFor(annotation = Component.class)
	String value() default "";
}

  可以看出,这两个注解都是@Component的别名,为了好看而已。
  使用的时候,肯定是先扫描配置文件,配置文件的内容就很简洁了,首先就是告诉程序要去扫描哪些包的注解,<context:component-scan base-package="cn.klb"></context:component-scan>就告诉程序:我的bean注解都在这些包里面。

2、AOP配置

  xml配置文件的AOP配置是在xml文件中定义<aop:config>标签,标签体定义通知类的方法和需要增强的表达式。注解配置的实现功能是一样的,只是换个地方配置,<aop:config>标签换成在通知类上,表达式和通知类型都定义在这个类的注解上。

2.1 创建maven工程

 projectName
	|-src
		|-main
			|-java
				|-cn.klb
					|-dao
						|-AccountDao
						|-impl
							|-AccountDaoImpl
					|-service
						|-AccountService
						|-impl
							|-AccountServiceImpl
					|-utils
						|-Logger
				|-resource
					|-bean.xml

2.2 导入jar坐标

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>
    </dependencies>

  同理,maven会自动导入间接依赖
在这里插入图片描述

2.3 实体类

  实体类内容和第一节一样。

2.4 配置文件

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

    <!-- 告知 spring 创建容器时要扫描的包 -->
    <context:component-scan base-package="cn.klb"></context:component-scan>

    <!--开启spring对注解AOP的支持-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>

  当大部分的IOC交给注解了,那么配置文件只剩一个作用:开启xx。

2.5 AOP注解配置

  原来的AOP的xml配置文件流程大体如下:

	<!-- 配置Logger类 -->
    <bean id="logger" class="cn.klb.utils.Logger"></bean>

    <!--配置AOP-->
    <aop:config>
        <!-- 配置切入点表达式 id属性用于指定表达式的唯一标识。expression属性用于指定表达式内容
              此标签写在aop:aspect标签内部只能当前切面使用。
              它还可以写在aop:aspect外面,此时就变成了所有切面可用
          -->
        <aop:pointcut id="pt1" expression="execution(* cn.klb.service.impl.*.*(..))"></aop:pointcut>
        <!--配置切面 -->
        <aop:aspect id="logAdvice" ref="logger">
            <!-- 配置前置通知:在切入点方法执行之前执行-->
            <aop:before method="beforePrintLog" pointcut-ref="pt1"></aop:before>

            <!-- 配置后置通知:在切入点方法正常执行之后值。它和异常通知永远只能执行一个-->
            <!-- <aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1"></aop:after-returning>-->

            <!-- 配置异常通知:在切入点方法执行产生异常之后执行。它和后置通知永远只能执行一个-->
            <!-- <aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1"></aop:after-throwing>-->

            <!-- 配置最终通知:无论切入点方法是否正常执行它都会在其后面执行-->
            <!-- <aop:after method="afterPrintLog" pointcut-ref="pt1"></aop:after>-->
        </aop:aspect>
    </aop:config>

  可见,AOP至少两个部分:通知类+被增强的表达式。
  Logger类:

package cn.klb.utils;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * 用于记录日志的工具类,它里面提供了公共的代码
 */
@Component("txManager")
@Aspect
public class Logger {

    // 定义切入点表达式的引用
    @Pointcut("execution(* cn.klb.service.impl.*.*(..))")
    private void pt1(){}

    /**
     * 前置通知
     */
    @Before("pt1()")
    public  void beforePrintLog(){

        System.out.println("前置通知Logger类中的beforePrintLog方法开始记录日志了。。。");
    }
}

2.6 测试类

package cn.klb;

import cn.klb.service.AccountService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @Author: Konglibin
 * @Description:
 * @Date: Create in 2020/3/10 13:28
 * @Modified By:
 */
public class AnnotationTest {

    @Test
    public void testSpringIOC(){
        //1.使用 ApplicationContext 接口,就是在获取 spring 容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");

        //2.根据 bean 的 id 获取对象
        AccountService aService = (AccountService) ac.getBean("accountServiceImpl");
        aService.saveAccount();

    }
}

  测试结果:
在这里插入图片描述

3、事务控制

  事务控制其实本质也是AOP。上面的注解配置AOP写道,把AOP的注解配置都写在通知类当中,而spring的事务是框架做好了的,所以我们是把事务的AOP配置在待增强的方法、类或接口当中。
  准备一个数据库表:
在这里插入图片描述

3.1 创建maven工程

 projectName
	|-src
		|-main
			|-java
				|-cn.klb
					|-dao
						|-AccountDao
						|-impl
							|-AccountDaoImpl
					|-service
						|-AccountService
						|-impl
							|-AccountServiceImpl
				|-resource
					|-bean.xml

3.2 导入jar包坐标

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

  这里涉及到junit整合spring的内容,自行百度。

3.3 配置文件

<?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:tx="http://www.springframework.org/schema/tx"
       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/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 配置spring创建容器时要扫描的包-->
    <context:component-scan base-package="cn.klb"></context:component-scan>

    <!-- 配置JdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 配置数据源-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/ssm"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>

    <!-- spring中基于注解 的声明式事务控制配置步骤
        1、配置事务管理器
        2、开启spring对注解事务的支持
        3、在需要事务支持的地方使用@Transactional注解
     -->
    <!-- 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <!-- 开启spring对注解事务的支持-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

</beans>

3.4 程序源码

  持久层

package cn.klb.dao;

import cn.klb.domain.Account;

/**
 * 账户的持久层接口
 */
public interface IAccountDao {

    /**
     * 根据Id查询账户
     * @param accountId
     * @return
     */
    Account findAccountById(Integer accountId);

    /**
     * 根据名称查询账户
     * @param accountName
     * @return
     */
    Account findAccountByName(String accountName);

    /**
     * 更新账户
     * @param account
     */
    void updateAccount(Account account);
}
package cn.klb.dao.impl;

import cn.klb.dao.IAccountDao;
import cn.klb.domain.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * 账户的持久层实现类
 */
@Repository("accountDao")
public class AccountDaoImpl implements IAccountDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public Account findAccountById(Integer accountId) {
        List<Account> accounts = jdbcTemplate.query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),accountId);
        return accounts.isEmpty()?null:accounts.get(0);
    }

    @Override
    public Account findAccountByName(String accountName) {
        List<Account> accounts = jdbcTemplate.query("select * from account where name = ?",new BeanPropertyRowMapper<Account>(Account.class),accountName);
        if(accounts.isEmpty()){
            return null;
        }
        if(accounts.size()>1){
            throw new RuntimeException("结果集不唯一");
        }
        return accounts.get(0);
    }

    @Override
    public void updateAccount(Account account) {
        jdbcTemplate.update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());
    }
}

  业务层

package cn.klb.service;

import cn.klb.domain.Account;

/**
 * 账户的业务层接口
 */
public interface IAccountService {
    /**
     * 根据id查询账户信息
     * @param accountId
     * @return
     */
    Account findAccountById(Integer accountId);

    /**
     * 转账
     * @param sourceName    转成账户名称
     * @param targetName    转入账户名称
     * @param money         转账金额
     */
    void transfer(String sourceName,String targetName,Float money);
}
package cn.klb.service.impl;

import cn.klb.dao.IAccountDao;
import cn.klb.domain.Account;
import cn.klb.service.IAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 * 账户的业务层实现类
 *
 * 事务控制应该都是在业务层
 */
@Service("accountService")
@Transactional(propagation= Propagation.SUPPORTS,readOnly=true)//只读型事务的配置
public class AccountServiceImpl implements IAccountService{

    @Autowired
    private IAccountDao accountDao;

    @Override
    public Account findAccountById(Integer accountId) {
        return accountDao.findAccountById(accountId);
    }

    //需要的是读写型事务配置
    @Transactional(propagation= Propagation.REQUIRED,readOnly=false)
    @Override
    public void transfer(String sourceName, String targetName, Float money) {
        System.out.println("transfer....");
            //2.1根据名称查询转出账户
            Account source = accountDao.findAccountByName(sourceName);
            //2.2根据名称查询转入账户
            Account target = accountDao.findAccountByName(targetName);
            //2.3转出账户减钱
            source.setMoney(source.getMoney()-money);
            //2.4转入账户加钱
            target.setMoney(target.getMoney()+money);
            //2.5更新转出账户
            accountDao.updateAccount(source);

            int i=1/0;

            //2.6更新转入账户
            accountDao.updateAccount(target);
    }
}

  Account实体类

package cn.klb.domain;

import java.io.Serializable;

/**
 * 账户的实体类
 */
public class Account implements Serializable {

    private Integer id;
    private String name;
    private Float money;

	//省略set get的书写
}

3.5 测试类

package cn.klb.test;

import cn.klb.service.IAccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * 使用Junit单元测试:测试我们的配置
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class AccountServiceTest {

    @Autowired
    private  IAccountService as;

    @Test
    public  void testTransfer(){
        as.transfer("aaa","bbb",100f);
    }
}

  业务层人为加一个异常,运行结果:
在这里插入图片描述
  数据表无改变。可以,事务管理生效了。

4、纯注解开发

  经过那么多注解,spring的配置文件只剩下:告诉spring要扫描的包、开启AOP、开启事务等一些开关性的工作和配置数据源等。如果全使用注解配置,意味着这些公共配置要交给一个类来代替。

@Configuration
@EnableTransactionManagement
public class SpringTxConfiguration {
	//里面配置数据源,配置 JdbcTemplate,配置事务管理器。
}

  定义了这个类后,要调用spring管理的所有东西,就不需要加载bean.xml配置文件,转为加载这个类即可。
  其实spring的核心作用就是解耦,可以把程序分成若干“组件”,通过配置文件进行组合更替,如果全都是注解,一旦要改点什么,还不是要来改源码,违背了解耦的初衷。其实纯注解开发用的较少,用最多的就是配置文件和注解混合开发,一些固定的配置用注解,灵活的配置写在配置文件中。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值