• 第二天:Spring整合各种数据源+基于注解的IOC配置+动态代理


    一、Spring 整合 DbUtils

    (一)Spring 整合 DbUtils

    DbUtilsApache的一款用于简化Dao代码的工具类,它底层封装了JDBC技术。 核心类:QueryRunner 用于执行增删改查的SQL语句 ResultSetHandler 这是一个接口,主要作用是将数据库返回的记录封装进实体对象

    1、导入依赖的坐标

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.3.7.RELEASE</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>4.3.7.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.7</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.15</version>
        </dependency>

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

        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>

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

     

    2、创建数据表

    create table Person(

    pid int primary key auto_increment,

    pname varchar(100) not null unique,

    Paddress varchar(100) not null

    )

    3、创建实体类

    public class Person implements Serializable {

    private Integer pid;

    private String pname;  

    private String paddress;

    }

    4、编写持久层代码

    public interface PersonDao {

    //保存

    void save(Person person );

    //根据主键查询

    Person findByPid(Integer pid);

    //查询所有

    List<Account> findAll();

    }

    public class PersonDao Impl implements PersonDao {

    QueryRunner queryRunner=null;

    public void setQueryRunner(QueryRunner queryRunner) {

    this.queryRunner = queryRunner;

    }

    @Override

    public void save(Person person) {

    try {

    queryRunner.update("insert into person(pname,paddress)value(?,?)",

    account.getPname(),

    account.getAddress());  

    } catch (SQLException e) {

    e.printStackTrace();

    throw new RuntimeException(e);

    }; 

    }

    @Override

    public Person findByAid(Integer pid) {

    try {

    //返回值封装到第二个参数位置

    return queryRunner.query("select * from person where pid = ?",

    new BeanHandler<>(Person.class), pid );

    } catch (SQLException e) {

     e.printStackTrace();

    throw new RuntimeException(e);

    }

    }

    @Override

    public List<Person> findAll() {

    try {

    //返回值封装到第二个参数位置

    return queryRunner.query("select * from person",

    new BeanListHandler<>(Person.class) );

    } catch (SQLException e) {

    e.printStackTrace();

    throw new RuntimeException(e);

    }

    }

    }

     

    5、编写业务层代码

    public interface PersonService {

    //保存

    void save(Person person);

    //根据主键查询

    Person findByAid(Integer pid);

    //查询所有

    List<Person> findAll();

    }

     

    public class AccountServiceImpl implements AccountService {

    PersonDao personDao;

    public void setPersonDao(PersonDao personDao) {

    this.personDao= personDao;

    }

    @Override

    public void save(Person person) {

    personDao.save(person);

    }

    @Override

    public Person findByAid(Integer pid) {

    return personDao.findByAid(pid);

    }

    @Override

    public List<Person> findAll() {

    return personDao.findAll();

    }

    }

     

    6、创建配置文件

    <?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 https://www.Springframework.org/schema/beans/Spring-beans.xsd http://www.Springframework.org/schema/context https://www.Springframework.org/schema/context/Spring-context.xsd">

    <!--datasource-->

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">

    <property name="driverClassName" value="com.MySQL.jdbc.Driver"/>

    <property name="url" value="jdbc:MySQL://localhost:3306/Spring02"/>

    <property name="username" value="root"/>

    <property name="password" value="adminadmin"/>

    </bean>

    <!--queryRunner-->

    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">

    <constructor-arg name="ds" ref="dataSource"/>

    </bean>

    <!--PersonDaoImpl-->

    <bean id="personDao" class="com.offcn.dao.impl.PersonDaoImpl">

    <property name="queryRunner" ref="queryRunner"/>

    </bean>

    <!--PersonServiceImpl-->

    <bean id="personService" class="com.offcn.service.impl.PersonServiceImpl">

    <property name="personDao" ref="personDao"/>

    </bean>

    </beans>

    7、测试代码

    public  class  PersonServiceTest {

    ApplicationContext applicationContext =

    new ClassPathXmlApplicationContext("applicationContext.xml");

    PersonService personService =

    (PersonService ) applicationContext.getBean("personService");

     

    //测试保存

    @Test

    public void testSave() {

    Person person = new Person();

    person.setPname("张三");

    person.setAddress("济南");

    personService .save(person);

    }

    //测试查询

    @Test

    public void testFindByAid() {

    Person person= personService.findByAid(2);

    System.out.println(person);

    }

    //测试查询所有

    @Test

    public void testFindAll() {

    List<Person> personList = personService.findAll();

    for (Person person: personList ) {

    System.out.println(person);

    }

    }

    }

    二、Spring整合各种数据源

    Spring中配置数据源非常简单,只要两步:

    1、引入数据源坐标

    2、在配置文件中配置DataSourcebean

    (一)整合C3P0数据源

    1、添加依赖坐标:

    <dependency>

    <groupId>com.mchange</groupId>

    <artifactId>c3p0</artifactId>

    <version>0.9.5.4</version>

    </dependency>

     

    2、在Spring配置文件中添加配置内容

    <bean id="comboPooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">

    <property name="driverClass" value="数据库驱动名称"/>

    <property name="jdbcUrl" value="数据库url地址"/>

    <property name="user" value="数据库名称"/>

    <property name="password" value="数据库密码"/>

    </bean>

     

    (二)Spring-jdbc 自带数据源

    Spring自带的数据源,没用到池技术,适合开发、测试环境使用

     

    1、添加依赖坐标

    <dependency>

    <groupId>org.Springframework</groupId>

    <artifactId>Spring-jdbc</artifactId>

    <version>5.1.5.RELEASE</version>

    </dependency>

     

    2、在配置文件中添加数据源的配置

    <bean

    id="dataSource" class="org.Springframework.jdbc.datasource.DriverManagerDataSource">

    <property name="driverClass" value="数据库驱动名称"/>

    <property name="jdbcUrl" value="数据库url地址"/>

    <property name="user" value="数据库名称"/>

    <property name="password" value="数据库密码"/> 

    </bean>

    (三)阿里巴巴数据源 Druid

    1、添加依赖坐标

    <dependency>

    <groupId>com.alibaba</groupId>

    <artifactId>druid</artifactId>

    <version>1.1.15</version>

    </dependency>

     

    2、在配置文件中添加数据源的配置

    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">

    <property name="driverClass" value="数据库驱动名称"/>

    <property name="jdbcUrl" value="数据库url地址"/>

    <property name="user" value="数据库名称"/>

    <property name="password" value="数据库密码"/> 

    </bean>

    (四)DBCP数据源

    1、添加依赖坐标

    <dependency>

    <groupId>org.apache.commons</groupId>

    <artifactId>commons-dbcp2</artifactId>

    <version>2.6.0</version>

    </dependency>

    2、在配置文件中添加数据源的配置信息

    <bean id="basicDataSource" class="org.apache.commons.dbcp2.BasicDataSource">

    <property name="driverClass" value="数据库驱动名称"/>

    <property name="jdbcUrl" value="数据库url地址"/>

    <property name="user" value="数据库名称"/>

    <property name="password" value="数据库密码"/>

    </bean>

    三、基于注解的IOC配置

    ()常用注解介绍

    1、对象实例化注解

    @Component

    用于实例化对象,相当于配置文件中的< bean id="" class=""/> 它支持一个属性value,相当于xmlbeanid。如果不写,默认值为类名的首字母小写

    @Controller

    @Service

    @Repository

    这三个注解的功能跟@Component完全一样,只不过他们三个比较有语义化。

    @Controller 一般标注在变现层的类上

    @Service 一般标注在业务层的类上

    @Repository 一般标注在持久层的类上

    推荐使用这三个,当一个类实在不好归属在这三个层上时,再使用@Component!!!

     

    2、依赖注入的注解

    @Autowired 按照类型注入。

    相当于配置文件中的< property name="" ref="">

    当使用注解注入属性时,set 方法可以省略。

    当要注入的属性存在多个实现类时会报错。

    @Qualifier

    @Autowired注入的基础之上,再按照 Bean id 注入。

    一般在@Autowired注入存在多个实现类的时候,使用@Qualifier按照beanid选取

    @Resource 直接按照 Bean id 注入

    @Value

    用于简单数据类型的注入,相当于< property name="" value="" >

     

    3、用于改变注入范围的注解

    @Scope

    用于指定bean的作用范围,相当于配置文件中的< bean scope="">

    (二)基于注解的IOC配置

    1、创建工程引入坐标

    <dependencies>

    <dependency>

    <groupId>org.Springframework</groupId>

    <artifactId>Spring-context</artifactId>

    <version>5.1.5.RELEASE</version>

    </dependency>

    </dependencies>

     

    2、使用@Component注解配置管理的资源

    //使用@ComponentSpring声明一个bean

    //相当于xml<bean id="book" class="com.itheima.anno.Book" />

    @Component("book")

    public class Book {

    private int bid;

    private String bname;

    private double bprice;

    }

     

    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:context="http://www.Springframework.org/schema/context" xsi:schemaLocation="http://www.Springframework.org/schema/beans https://www.Springframework.org/schema/beans/Spring-beans.xsd http://www.Springframework.org/schema/context " >https://www.Springframework.org/schema/context/Spring-context.xsd">

     <!-- 告诉 Spring 创建容器时要扫描的包,可以理解为寻找注解的过程 --> <context:component-scan base-package="com.offcn"/>

    </beans>

     

    4、测试

    public class TestSpringAnno {

    ApplicationContext applicationContext =

     new ClassPathXmlApplicationContext("applicationContext.xml");

    @Test public void test() {

    Book book = (Book) applicationContext.getBean("book");

    book.desc();

    }

    }

    (三) 注解案例

    1、基于注解的CRUD

    拷贝原来的案例工程进行修改

     

    (1)使用注解修改持久层内容

    @Repository("personDao")

    public class PersonDaoImpl implements PersonDao {

    @Autowired

    private QueryRunner queryRunner;

    //其余代码跟原来一模一样

    }

    (2)使用注解修改业务层

    @Service("personService")

    //使用注解完成bean的声明

    //相当于xml中的

    <bean id="personService" class="com.offcn.dao.impl.PersonServiceImpl">

    public class PersonServiceImpl implements PersonService {

    //使用注解完成依赖注入

    @Autowired

    private PersonDao personDao;

    //其余代码跟原来一模一样

    }

    3)调整配置文件,去掉关于servicedaobean

    <?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 https://www.Springframework.org/schema/beans/Spring-beans.xsd http://www.Springframework.org/schema/context " >https://www.Springframework.org/schema/context/Spring-context.xsd">

    <context:component-scan base-package="com.offcn" />

    <!--datasource-->

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">

    <property name="driverClassName" value="com.MySQL.jdbc.Driver"/>

    <property name="url" value="jdbc:MySQL://localhost:3306/Spring02"/>

    <property name="username" value="root"/>

    <property name="password" value="adminadmin"/>

    </bean>

    <!--queryRunner-->

    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">

    <constructor-arg name="ds" ref="dataSource"/>

    </bean>

    </beans>

     

    4)测试代码

    使用原来的代码即可

     

    四、Spring和junit的整合

    这是Spring提供的对Junit单元测试的一种支持。使用步骤如下:

    1:引入坐标

    <dependency>

    <groupId>org.Springframework</groupId>

    <artifactId>Spring-test</artifactId>

    <version>5.1.5.RELEASE</version>

    </dependency>

     

    2:在测试类上使用@RunWith指定Spring的单元测试运行器

    @RunWith(SpringJUnit4ClassRunner.class)

    public class PersonServiceTest {

    }

     

    3:使用@ContextConfiguration指定配置文件,它支持文件和类的形式

    @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml")

    public class PersontServiceTest {

     

    }

    4:下面可以直接使用@Autowired的形式,让Spring容器为我们注入需要的bean

    @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml")

    public class PersonServiceTest {

    @Autowired

    PersonService personService

    }   

     

    五、动态代理模式详解

    (一)动态代理模式简介

    代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性

    代理类在程序运行时创建的代理方式被成为动态代理。动态代理是在运行时根据我们在Java代码中的指示动态生成的。相比于静态代理,动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。

    (二) 动态代理模式入门案例

    需求: A账户需要转账给B账户

     

    1Version 1.0 使用传统的分层调用方式,同时在service中添加事务管理

    1)构建我们的Dao层代码

    public class AccountDaoImpl implements AccountDao {

    //创建QueryRunner的时候不再传入数据源

    private QueryRunner queryRunner = new QueryRunner();

    private Connection connection;

    //在构造方法中传入Connection,这样就可以保证SQL语句使用的是同一个connection

    public AccountDaoImpl(Connection connection) {

    this.connection = connection;

    }

    @Override

    public Account findByName(String name) {

    //在执行的时候传入connectionconnection相同,事务就相同

    return queryRunner.query(connection,"select * from account where name = ?",

    new BeanHandler<Account>(Account.class), name);

    }

    @Override

    public void updateByName(Account account) {

    queryRunner.update(connection,"update account set balance = ? where name = ?",

    account.getBalance(), account.getName());

    }

    }

     

    2)构建Service服务层代码

    public class AccountServiceImpl implements AccountService {

    Connection connection = DataSourceUtil.getConnection();

    private AccountDao accountDao = new AccountDaoImpl(connection);

    @Override

    public void transfer(String sourceAccountName, String targetAccountName, Float amount) {

    try {

    connection.setAutoCommit(false);

    //查询余额

    Account sourceAccount = accountDao.findByAccountName(sourceAccountName);

    Account targetAccount = accountDao.findByAccountName(targetAccountName);

    //增减

    sourceAccount.setBalance(sourceAccount.getBalance() - amount);

    targetAccount.setBalance(targetAccount.getBalance() + amount);

    //更新余额

    accountDao.update(sourceAccount);

    //int i = 1 / 0;

    accountDao.update(targetAccount);

    connection.commit();

    } catch (Exception e) {

    System.out.println("业务出现异常");

    try {

    connection.rollback();

    } catch (SQLException ex) {

    ex.printStackTrace();

    }

    }

    }

    }

    代码问题:Connection是数据库连接,属于持久层的东西,但现在却跑到了业务层里,又出现了层次混乱。

     

    2V2.0 从业务层剥离跟持久层相关的代码

    使用ThreadLocal从业务层剥离跟持久层相关的代码

     

    1创建一个事务管理器的工具类,使用ThreadLocal存放Connection

    public class TxManager {

    private static ThreadLocal<Connection> tl = new ThreadLocal<>();

    //获取连接

    public static Connection getConnection() {

    Connection connection = tl.get();

    if (connection == null) {

    connection = DataSourceUtil.getConnection();

    tl.set(connection);

    }

    return connection;

    }

    //开启事务

    public static void begin() {

    try {

    getConnection().setAutoCommit(false);

    } catch (SQLException e) {

    e.printStackTrace();

    }

    }

    //提交事务

    public static void commit() {

    try {

    getConnection().commit();

    } catch (SQLException e) {

    e.printStackTrace();

    }

    }

    //回滚事务

    public static void rollback() {

    try {

    getConnection().rollback();

    } catch (SQLException e) {

    e.printStackTrace();

    }

    }

    //关闭事务

    public static void close() {

    try {

    getConnection().close();

    tl.remove();

    } catch (SQLException e) {

    e.printStackTrace();

    }

    }

    }

    2)构建service服务层代码:

    public class AccountServiceImpl implements AccountService {

    private AccountDao accountDao = new AccountDaoImpl();

    @Override

    public void transfer(String sourceAccountName, String targetAccountName, Float amount) {

    try {

    TxManager.begin();

    //查询余额

    Account sourceAccount = accountDao.findByAccountName(sourceAccountName);

    Account targetAccount = accountDao.findByAccountName(targetAccountName);

    //增减

    sourceAccount.setBalance(sourceAccount.getBalance() - amount);

    targetAccount.setBalance(targetAccount.getBalance() + amount);

    //更新余额

    accountDao.update(sourceAccount);

    int i = 1 / 0;

    accountDao.update(targetAccount);

    TxManager.commit();

    } catch (Exception e) {

    System.out.println("业务出现异常");

    TxManager.rollback();

    } finally {

    TxManager.close();

    }

    }

    }

     

    3)构建数据操作层Dao代码

    public class AccountDaoImpl implements AccountDao {

    private QueryRunner queryRunner = new QueryRunner();

    @Override

    public Account findByAccountName(String accountName) {

    try {

    return queryRunner.query(TxManager.getConnection(),

    "select * from account where name = ?",

    new BeanHandler<>(Account.class), accountName);

    } catch (Exception e) {

    throw new RuntimeException(e);

    }

    }

    @Override

    public void update(Account account) {

    try {

    queryRunner.update(TxManager.getConnection(), "update account set balance = ?         where name = ?",

    account.getBalance(), account.getName());

    } catch (Exception e) {

    throw new RuntimeException(e);

    }

    }

    }

    代码问题:在业务层的代码中存在一些跟业务无关 、跟事务相关的公共代码。

     

    3V3.0 使用动态代理优化代码

    1GDK动态代理是基于接口实现的

     

    a.创建动态代理类

    //动态代理类

    public class AccountServiceProxyImpl implements InvocationHandler {

    private AccountService target;

    //传进来的就是被代理类

    public AccountServiceProxyImpl(AccountService target) {

    this.target = target;

    }

    @Override

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

    try {

    TxManager.begin();

    //调被代理类实现核心业务

    //target.transfer(sourceAccountName, targetAccountName, amount);

    method.invoke(target,args);

    TxManager.commit();

    } catch (Exception e) {

    System.out.println("业务出现异常");

    TxManager.rollback();

    } finally {

    TxManager.close();

    }

    return null;

    }

    }

    b.测试

    public class AccountTest {

    private AccountService accountService = new AccountServiceImpl();

    @Test

    public void testTransfer() {

    //ClassLoader loader, 类加载器,因为代理类是运行时才真正产生的,所以需要类

    加载器的加载。跟被代理类使 用同一个

    //Class<?>[] interfaces, 接口,就是用被代理类实现的接口,目的是使得代理类和被

    代理类拥有相同的方法

    //InvocationHandler h 代理策略,里面是我们的道理逻辑

    AccountService instance = (AccountService) Proxy.newProxyInstance(

    accountService.getClass().getClassLoader(),

    accountService.getClass().getInterfaces(),

    new AccountServiceProxyImpl(accountService)

    );

    instance.transfer("A01","A02",2F);

    }

    }

     

    代码问题:GDK动态代理要求被代理类一定要有接口,当没有接口时便无法使用了。

     

    (2)CGLIB动态代理

    CGLIB动态代理是基于子类实现的

     

    a.添加依赖

    <dependency>

    <groupId>cglib</groupId>

    <artifactId>cglib</artifactId>

    <version>3.2.12</version>

    </dependency>

     

    b.构建动态代理工具类

    public class AccountServiceProxyImpl implements InvocationHandler {

    private AccountServiceImpl target;

    //传进来的就是被代理类

    public AccountServiceProxyImpl(AccountServiceImpl target) {

    this.target = target;

    }

    @Override

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

    try {

    TxManager.begin();

    //调被代理类实现核心业务

    //target.transfer(sourceAccountName, targetAccountName, amount);

    method.invoke(target,args);

    TxManager.commit();

    } catch (Exception e) {

    System.out.println("业务出现异常");

    TxManager.rollback();

    } finally {

    TxManager.close();

    }

    return null;

    }

    }

     

    c.测试

    public class AccountTest {

    private AccountServiceImpl accountService = new AccountServiceImpl();

    @Test

    public void testTransfer() {

    Enhancer enhancer = new Enhancer();

    //设置父类

    enhancer.setSuperclass(AccountServiceImpl.class);

    //设置增强

    enhancer.setCallback(new AccountServiceProxyImpl(accountService));

    //获取代理后的实例

    AccountServiceImpl instance = (AccountServiceImpl) enhancer.create();

    instance.transfer("A01", "A02", 2F);

    }

    }

     

    通过上面的案例,我们体会到一种新的编程思想:

    当核心业务(转账)和增强业务(事务)同时出现时,我们可以在开发时对他们分别开发,运行时再组装在一起(使用动态代理的方式)。

    这样做的好处是:

    1. 逻辑清晰,开发核心业务的时候,不必关注增强业务的代码

    2. 代码复用性高:增强代码不用重复书写

    这就是一种AOP的思想。

     

     

  • 相关阅读:
    景瑞地产商业智能BI整体实施过程
    域名访问和IP访问问题
    sitemesh定义多个装饰器
    8.8.2 EXPLAIN Output Format 优化输出格式
    Python_List对象内置方法详解
    Python_List对象内置方法详解
    Python_序列对象内置方法详解_String
    Python_序列对象内置方法详解_String
    CentOS设置服务开机启动的两种方法
    perl 没有关键文件句柄引起的逻辑错误
  • 原文地址:https://www.cnblogs.com/masterhxh/p/13840903.html
Copyright © 2020-2023  润新知