ssm-Spring-从零学习spring,少部分代码为图片

文章目录

spring学习

----------------------xml

1、spring的概述
spring是什么

Spring是分层的 Java SE/EE应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control:

反转控制)和 AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层 SpringMVC 和持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE 企业应用开源框架。

spring的两大核心

IOC和AOP,也就是依赖注入和面向切面编程

spring的发展历程

1997 年 IBM提出了EJB 的思想

1998 年,SUN制定开发标准规范 EJB1.0

1999 年,EJB1.1 发布

2001 年,EJB2.0 发布

2003 年,EJB2.1 发布

2006 年,EJB3.0 发布

Rod Johnson ( spring 之父)

Expert One-to-One J2EE Design and Development(2002)

阐述了 J2EE 使用EJB 开发设计的优点及解决方案

Expert One-to-One J2EE Development without EJB(2004)

阐述了 J2EE 开发不使用 EJB的解决方式(Spring 雏形)

7 2017 年 9 9 月份发布了 g spring 的最新版本 0 spring 5.0 通用版(GA)

spring 的优势

方便解耦,简化开发

通过 Spring提供的 IoC容器,可以将对象间的依赖关系交由 Spring进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。

AOP 编程的支持

通过 Spring的 AOP 功能,方便进行面向切面的编程,许多不容易用传统OOP 实现的功能可以通过 AOP 轻松应付。

声明式事务的支持

可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量。

spring体系结构

在这里插入图片描述

IOC概念和spring中的IOC
  • 概念解析
    • IoC主要体现两方面:控制&反转,简单而言就是某一接口具体实现类的选择控制权从调用类中移除,转交到第三方决定。IoC在Spring具体体现为依赖注入。
  • 原理解析
    • BeanFactory是Spring框架最核心的接口,它提供了高级IoC的配置机制。BeanFactory使管理不同类型的Java对象成为可能,ApplicationContext建立在BeanFactory基础之上,提供了更多面向应用的功能。它提供了国际化支持和框架事件体系,更易于创建实际应用。一般称BeanFactory为IoC容器,而称ApplicationContext为应用上下文(Spring容器)。
2、程序的耦合及解耦
曾经案例中问题

分析直接依赖new的问题

工厂模式解耦
public class BeanFactory {
    private static Properties props;

    private static Map<String,Object> beans;

    static {
        try {
            props = new Properties();
            InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
            props.load(in);
            beans = new HashMap<String, Object>();
            Enumeration<Object> keys = props.keys();
            while (keys.hasMoreElements()){
                String key = keys.nextElement().toString();
                Object value = Class.forName(props.getProperty(key)).newInstance();
                beans.put(key,value);

            }
        } catch (Exception e) {
            throw new ExceptionInInitializerError("初始化properties失败!");
        }
    }

    public static Object getBean(String beanName){
        Object bean=null;
        bean = beans.get(beanName);
        return bean;
    }


//    public static Object getBean(String beanName){
//        Object bean = null;
//        try {
//            String beanPath = props.getProperty(beanName);
//            bean  = Class.forName(beanPath).newInstance();
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//        return bean;
//    }
}
3、spring中XML方式Bean的常用属性
创建Bean对象的三钟方法

​ 第一种方式:使用默认构造函数创建。
​ 在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时。
​ 采用的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数,则对象无法创建。

<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"/>

​ 第二种方式: 使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)

 <bean id="instanceFactory" class="com.itheima.factory.InstanceFactory"></bean>
    <bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>

​ 第三种方式:使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)

 <bean id="accountService" class="com.itheima.factory.StaticFactory" factory-method="getAccountService"></bean>
bean的作用范围调整

​ bean标签的scope属性:
​ 作用:用于指定bean的作用范围
​ 取值: 常用的就是单例的和多例的
​ singleton:单例的(默认值)
​ prototype:多例的
​ request:作用于web应用的请求范围
​ session:作用于web应用的会话范围
​ global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session

bean对象的生命周期

​ 单例对象
​ 出生:当容器创建时对象出生
​ 活着:只要容器还在,对象一直活着
​ 死亡:容器销毁,对象消亡
​ 总结:单例对象的生命周期和容器相同
​ 多例对象
​ 出生:当我们使用对象时spring框架为我们创建
​ 活着:对象只要是在使用过程中就一直活着。
​ 死亡:当对象长时间不用,且没有别的对象引用时,由Java的垃圾回收器回收

<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"
         init-method="init" destroy-method="destroy" scope="singleton"/>
获取spring的Ioc核心容器,并根据id获取对象

ApplicationContext的三个常用实现类:
ClassPathXmlApplicationContext:它可以加载类路径下的配置文件,要求配置文件必须在类路径下。不在的话,加载不了。(更常用)
FileSystemXmlApplicationContext:它可以加载磁盘任意路径下的配置文件(必须有访问权限)

​ AnnotationConfigApplicationContext:它是用于读取注解创建容器的,是明天的内容。

核心容器的两个接口引发出的问题:
ApplicationContext: 单例对象适用 采用此接口
它在构建核心容器时,创建对象采取的策略是采用立即加载的方式。也就是说,只要一读取完配置文件马上就创建配置文件中配置的对象。

BeanFactory: 多例对象使用
它在构建核心容器时,创建对象采取的策略是采用延迟加载的方式。也就是说,什么时候根据id获取对象了,什么时候才真正的创建对象。

4、依赖注入(Dependency Injection)
什么是依赖注入?

以后都交给spring来维护,在当前类需要用到其他类的对象,由spring为我们提供,我们只需要在配置文件中说明,依赖关系的维护:就称之为依赖注入。

依赖注入:

Dependency Injection。它是 spring 框架核心 ioc 的具体实现。

我们的程序在编写时,通过控制反转,把对象的创建交给了 spring,但是代码中不可能出现没有依赖的情况。

ioc 解耦只是降低他们的依赖关系,但不会消除。例如:我们的业务层仍会调用持久层的方法。依赖注入:Dependency Injection。它是 spring 框架核心 ioc 的具体实现。

我们的程序在编写时,通过控制反转,把对象的创建交给了 spring,但是代码中不可能出现没有依赖的情况。

ioc 解耦只是降低他们的依赖关系,但不会消除。例如:我们的业务层仍会调用持久层的方法。

在这里插入图片描述

1.构造方法注入

在这里插入图片描述

2.set方法注入

在这里插入图片描述

Bean.xml文件

在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YGFNMiD1-1598361252489)(G:\图片素材\java\clip_image006-1597197698867.jpg)]

3.复杂类型注入

编写AccountServiceImpl3,提供集合相关的类型

在这里插入图片描述

Bean.xml
在这里插入图片描述
在这里插入图片描述

----------------------anno

1、spring中ioc的常用注解
曾经XML的配置:
 <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"
       scope=""  init-method="" destroy-method="">
     <property name=""  value="" | ref=""></property>
 </bean>
用于创建对象的

​ 他们的作用就和在XML配置文件中编写一个标签实现的功能是一样的
​ Component:
​ 作用:用于把当前类对象存入spring容器中
​ 属性:
​ value:用于指定bean的id。当我们不写时,它的默认值是当前类名,且首字母改小写。
​ Controller:一般用在表现层
​ Service:一般用在业务层
​ Repository:一般用在持久层
​ 以上三个注解他们的作用和属性与Component是一模一样。
​ 他们三个是spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰

用于注入数据的

​ 他们的作用就和在xml配置文件中的bean标签中写一个标签的作用是一样的
​ Autowired:
​ 作用:自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
​ 如果ioc容器中没有任何bean的类型和要注入的变量类型匹配,则报错。
​ 如果Ioc容器中有多个类型匹配时:
​ 出现位置:
​ 可以是变量上,也可以是方法上
​ 细节:
​ 在使用注解注入时,set方法就不是必须的了。
​ Qualifier:
​ 作用:在按照类中注入的基础之上再按照名称注入。它在给类成员注入时不能单独使用。但是在给方法参数注入时可以(稍后我们讲)
​ 属性:
​ value:用于指定注入bean的id。
​ Resource
​ 作用:直接按照bean的id注入。它可以独立使用
​ 属性:
​ name:用于指定bean的id。
​ 以上三个注入都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现。
​ 另外,集合类型的注入只能通过XML来实现。

​ Value
​ 作用:用于注入基本类型和String类型的数据
​ 属性:
​ value:用于指定数据的值。它可以使用spring中SpEL(也就是spring的el表达式)
​ SpEL的写法:${表达式}

用于改变bean对象创建

​ 他们的作用就和在bean标签中使用scope属性实现的功能是一样的
​ Scope
​ 作用:用于指定bean的作用范围
​ 属性:
​ value:指定范围的取值。常用取值:singleton prototype

和生命周期相关 了解

​ 他们的作用就和在bean标签中使用init-method和destroy-methode的作用是一样的
​ PreDestroy
​ 作用:用于指定销毁方法
​ PostConstruct
​ 作用:用于指定初始化方法

2、案例使用xml方式和注解方式实现单表的CRUD操作

​ 持久层技术选择:dbutils和c3p0

1.设置扫描注解的包

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

    <!--配置QueryRunner-->
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
        <!--注入数据源-->
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/eesy_spring"/>
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="user" value="root"/>
        <property name="password" value="root"/>
    </bean>

2.导入相应的jar包

3、改造基于注解的ioc案例,使用纯注解的方式实现
1.创建配置类
@Configuration   //声明此类为配置lei
@ComponentScan("com.itheima")  //需要扫描的包
@Import(JdbcConfig.class)     //引入子配置类
@PropertySource("classpath:jdbcConfig.properties")  //引入外部配置文件
public class SpringConfigration {
}

该类是一个配置类,它的作用和bean.xml是一样的
spring中的新注解
Configuration
作用:指定当前类是一个配置类
细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写。
ComponentScan
作用:用于通过注解指定spring在创建容器时要扫描的包
属性:
value:它和basePackages的作用是一样的,都是用于指定创建容器时要扫描的包。
我们使用此注解就等同于在xml中配置了:
<context:component-scan base-package=“com.itheima”></context:component-scan>
Bean
作用:用于把当前方法的返回值作为bean对象存入spring的ioc容器中
属性:
name:用于指定bean的id。当不写时,默认值是当前方法的名称
细节:
当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象。
查找的方式和Autowired注解的作用是一样的
Import
作用:用于导入其他的配置类
属性:
value:用于指定其他配置类的字节码。
当我们使用Import的注解之后,有Import注解的类就父配置类,而导入的都是子配置类
PropertySource
作用:用于指定properties文件的位置
属性:
value:指定文件的名称和路径。
关键字:classpath,表示类路径下

2.可以创建子配置类
public class JdbcConfig {

    @Value("${jdbc.driver}")
    private String driver;

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

    @Bean(name = "runner")
    @Scope("prototype")
    public QueryRunner getQueryRunner(@Qualifier("dataSources1") DataSource dataSources){
        return new QueryRunner(dataSources);
    }

    @Bean(name = "dataSources1")
    public DataSource getDataSources(){
        try {
            ComboPooledDataSource ds = new ComboPooledDataSource();
            ds.setDriverClass("com.mysql.jdbc.Driver");
            ds.setJdbcUrl("jdbc:mysql://localhost:3306/eesy_spring");
            ds.setUser("root");
            ds.setPassword("root");
            return ds;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Bean(name = "dataSources2")
    public DataSource getDataSources2(){
        try {
            ComboPooledDataSource ds = new ComboPooledDataSource();
            ds.setDriverClass("com.mysql.jdbc.Driver");
            ds.setJdbcUrl("jdbc:mysql://localhost:3306/eesy_spring2");
            ds.setUser("root");
            ds.setPassword("root");
            return ds;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
4、spring和Junit整合
1.分析

1、应用程序的入口
main方法
2、junit单元测试中,没有main方法也能执行
junit集成了一个main方法
该方法就会判断当前测试类中哪些方法有 @Test注解
junit就让有Test注解的方法执行
3、junit不会管我们是否采用spring框架
在执行测试方法时,junit根本不知道我们是不是使用了spring框架
所以也就不会为我们读取配置文件/配置类创建spring核心容器
4、由以上三点可知
当测试方法执行时,没有Ioc容器,就算写了Autowired注解,也无法实现注入

使用Junit单元测试:测试我们的配置

2.Spring整合junit的配置

​ 1、导入spring整合junit的jar(坐标)

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

​ 2、使用Junit提供的一个注解把原有的main方法替换了,替换成spring提供的
​ @Runwith

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfigration.class)
public class TestXmlSpringCRUD {

    @Autowired
    private AccountService accountService;

​ 3、告知spring的运行器,spring和ioc创建是基于xml还是注解的,并且说明位置
​ @ContextConfiguration
​ locations:指定xml文件的位置,加上classpath关键字,表示在类路径下
​ classes:指定注解类所在地位置

  • 注意:当我们使用spring 5.x版本的时候,要求junit的jar必须是4.12及以上

----------------------aop

1.层层深入:编写工具类实现事务管理
编写转账的代码
注:事务控制在业务层
            //2.1根据名称查询转出账户
            Account account1 = accountDao.findByName(sourceName);
            //2.2根据名称查询转入账户
            Account account2 = accountDao.findByName(targetName);
            //2.3转出账户减钱
            account1.setMoney(account1.getMoney()-money);
            //2.4转入账户加钱
            account2.setMoney(account2.getMoney()+money);
            //2.5更新转出账户
            accountDao.updateAccount(account1);
            int i=1/0;
            //2.6更新转入账户
            accountDao.updateAccount(account2);
编写ConnnectionUtils工具类

public class ConnectionUtils {
private ThreadLocal t1 = new ThreadLocal();

​ private DataSource dataSource;

​ public void setDataSource(DataSource dataSource) {
​ this.dataSource = dataSource;
​ }

​ public Connection getConnection(){
​ try {
​ Connection connection = t1.get();
​ if (connection == null){
​ connection = dataSource.getConnection();
​ t1.set(connection);
​ }
​ return connection;
​ }catch (Exception e) {
​ throw new RuntimeException(e);
​ }
​ }

​ public void removeConnection(){
​ t1.remove();
​ }
}

public class ConnectionUtils {
    private ThreadLocal<Connection> t1 = new ThreadLocal<Connection>();

    private DataSource dataSource;

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public Connection getConnection(){
        try {
            Connection connection = t1.get();
            if (connection == null){
                connection = dataSource.getConnection();
                t1.set(connection);
            }
            return connection;
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    public void removeConnection(){
        t1.remove();
    }
}
编写控制事务类
public class TransactionManager {

    private ConnectionUtils connectionUtils;

    public void setConnectionUtils(ConnectionUtils connectionUtils) {
        this.connectionUtils = connectionUtils;
    }
//开启事务 
    public  void beginTransaction(){
        try {
            connectionUtils.getConnection().setAutoCommit(false);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
//提交事务  //类似操作
   
//回滚事务

//释放连接   //此处还需调用connectionUtils的removeConnection方法清空ThreadLock
    
}
开启事务
public class AccountServiceImpl1 implements AccountService {
    private AccountDao accountDao;
    private TransactionManager manager;
//set方法用于xml中依赖注入
    public void setManager(TransactionManager manager) {
        this.manager = manager;
    }

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    public List<Account> findAll() {
        try{
            //开启事务
            manager.beginTransaction();//-------------------
            //执行操作
            //2.1根据名称查询转出账户
            Account account1 = accountDao.findByName(sourceName);
            //2.2根据名称查询转入账户
            Account account2 = accountDao.findByName(targetName);
            //2.3转出账户减钱
            account1.setMoney(account1.getMoney()-money);
            //2.4转入账户加钱
            account2.setMoney(account2.getMoney()+money);
            //2.5更新转出账户
            accountDao.updateAccount(account1);
//            int i=1/0;
            //2.6更新转入账户
            accountDao.updateAccount(account2);
            //提交事务
            manager.commit();//-------------------
        }catch (Exception e){ 
            //回滚事务
            manager.rollback();//-------------------
            throw new RuntimeException(e);
        }finally {
            //释放连接
            manager.release();//-------------------
        }
    }
 }
代码的问题

业务层方法变得臃肿了,里面充斥着很多重复代码。并且业务层方法和事务控制方法耦合了。
试想,如果我们此时提交,回滚,释放资源中任何一个方法名变更,都需要修改业务层的代码,况且这还
只是一个业务层实现类,而实际的项目中这种业务层实现类可能有十几个甚至几十个。

2.层层深入:使用动态代理事务控制
还是需编写工具栏和事务控制类

(同上个层层深入)

编写动态代理类

涉及类:Proxy接口的newProxyInstance方法

method.invoke(accountService, args)明确调用业务层方法

最终返回的是代理对象的增强后的对象

最终调用方法是也是使用此返回对象执行业务层方法

public class BeanFactry {
    private AccountService accountService;
//此处可使用注解自动注入
    private TransactionManager manager;

     // 获取Service代理对象
    public AccountService getAccountService() {
        return (AccountService)Proxy.newProxyInstance(accountService.getClass().getClassLoader(),
                accountService.getClass().getInterfaces(),
                new InvocationHandler() {

                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Object rtValue = null;
                        try {
                            //开启事务
                            manager.beginTransaction();
                            //执行操作
                            rtValue = method.invoke(accountService, args);
                            //提交事务
                            manager.commit();
                            return rtValue;
                        } catch (Exception e) {
                            //回滚事务
                            manager.rollback();
                            throw new RuntimeException(e);
                        } finally {
                            //释放连接
                            manager.release();
                        }
                    }
                });
        }
}
3.层层深入:spring的AOP
面向切面编程:作用,优势,实现方式

作用:在程序运行期间,不修改源码对已有方法进行增强。

优势:减少重复代码,提高开发效率,维护方便。

AOP 的实现方式:使用动态代理技术。

AOP相关术语:

Joinpoint( 连接点):所谓连接点是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为 spring 只支持方法类型的连接点。

Pointcut( 切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。

Advice( 通知/ 增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。

通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。

Introduction( 引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方法或 Field。

Target( 目标对象):代理的目标对象。

Weaving( 织入):是指把增强应用到目标对象来创建新的代理对象的过程。

spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。

Proxy(代理):一个类被 AOP 织入增强后,就产生一个结果代理类。

Aspect( 切面):是切入点和通知(引介)的结合。

spring中基于XML的AOP配置步骤

1、把通知通知类也交给spring来管理

2、使用aop:config标签表明开始AOP的配置

3、使用aop:aspect标签表明配置切面

​ id属性:是给切面提供一个唯一标识

​ ref属性:是指定通知类bean的Id。

4、在aop:aspect标签的内部使用对应标签来配置通知的类型

​ 我们现在示例是让printLog方法在切入点方法执行之前之前:所以是前置通知

​ aop:before:表示配置前置通知

​ method属性:用于指定Logger类中哪个方法是前置通知

pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中哪些方法增强

<!--配置AOP-->
<aop:config>
        <!-- 配置切入点表达式 id属性用于指定表达式的唯一标识。expression属性用
              于指定表达式内容
              此标签写在aop:aspect标签内部只能当前切面使用。
              它还可以写在aop:aspect外面,此时就变成了所有切面可用
          -->
     <aop:pointcut id="pt1" expression="execution(* com.itheima.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>

<!-- 配置环绕通知 详细的注释请看Logger类中-->
            <aop:around method="aroundPringLog" pointcut-ref="pt1"></aop:around>
        </aop:aspect>
    </aop:config>

Spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法proceed(),此方法就相当于明确调用切入点方法。

该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用。

spring中的环绕通知:

它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式。

在Logger通知类中新增环绕通知方法

public Object aroundPrintgLog(ProceedingJoinPoint pjp){
        Object rtValue = null;
        try{
            Object[] args = pjp.getArgs();//得到方法执行所需的参数
            System.out.println("Logger类中的aroundPringLog方法。。。前置");
            rtValue = pjp.proceed(args);//明确调用业务层方法(切入点方法)
            System.out.println("Logger类中的aroundPringLog方法。。。后置");
            return rtValue;
        }catch (Throwable t){
            System.out.println("Logger类中的aroundPringLog方法。。。异常");
            throw new RuntimeException(t);
        }finally {
            System.out.println("Logger类中的aroundPringLog方法。。。最终");
        }
    }
切入点表达式的写法

关键字:execution(表达式)

表达式:

​ 访问修饰符 返回值 包名.包名.包名…类名.方法名(参数列表)

标准的表达式写法:

​ public void com.itheima.service.impl.AccountServiceImpl.saveAccount()

访问修饰符可以省略

​ void com.itheima.service.impl.AccountServiceImpl.saveAccount()

返回值可以使用通配符,表示任意返回值

​ * com.itheima.service.impl.AccountServiceImpl.saveAccount()

包名可以使用通配符,表示任意包。但是有几级包,就需要写几个*.

​ * ....AccountServiceImpl.saveAccount())

包名可以使用…表示当前包及其子包

​ * *…AccountServiceImpl.saveAccount()

类名和方法名都可以使用*来实现通配

​ * .*()

参数列表:

​ 可以直接写数据类型:

​ 基本类型直接写名称 int

​ 引用类型写包名.类名的方式 java.lang.String

​ 可以使用通配符表示任意类型,但是必须有参数

​ 可以使用…表示有无参数均可,有参数可以是任意类型

全通配写法:

​ * .*(…)

实际开发中切入点表达式的通常写法:

​ 切到业务层实现类下的所有方法

​ * com.itheima.service.impl..(…)

基于注解的springAOP配置
//开启要扫描的包
<context:component-scan base-package="com.itheima"/>
//开启对spring注解aop的支持
<aop:aspectj-autoproxy/>
@Aspect     //表示当前类是一个切面类

@Pointcut    //配置一个可抽取的切入点表达式
/*   列:@Pointcut("execution(* com.itheima.service.impl.*.*(..))")
          private void pt1(){}   */
@Before("pt1()")     //前置通知
@AfterReturning("pt1()")   //后置通知
@AfterThrowing("pt1()")   //异常通知
@After("pt1()")  //最终通知
@Around("pt1()")   //环绕通知

----------------------aop++

1.jdbcTemplate的使用

JdbcTemplate的作用:

​ 它就是用于和数据库交互的,实现对表的CRUD操作JdbcTemplate的作用:

​ 它就是用于和数据库交互的,实现对表的CRUD操作

我们今天的主角在 spring-jdbc-5.0.2.RELEASE.jar 中,我们在导包的时候,除了要导入这个 jar 包外,还需要导入一个 spring-tx-5.0.2.RELEASE.jar(它是和事务相关的)

ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        JdbcTemplate jt = ac.getBean("jdbcTemplate",JdbcTemplate.class);
        //保存
      //  jt.update("INSERT into account VALUE (NULL ,'eee','1000')");
        //更新
      //  jt.update("UPDATE account SET NAME ='fff',money='1000' WHERE id = 15");
        //删除
       // jt.update("DELETE FROM account WHERE id=15");
        //查询所有
        List<Map<String, Object>> list = jt.queryForList("SELECT * from account");
        for (Map<String, Object> stringObjectMap : list) {
            System.out.println(stringObjectMap);
        }
        //使用较多
        //查询所有
        List<Account> accounts = jt.query("select * from account ",new BeanPropertyRowMapper<Account>(Account.class));
        for(Account account : accounts){
            System.out.println(account);
        }
        List<Account> accounts2 = jt.query("select * from account WHERE money>?",new BeanPropertyRowMapper<Account>(Account.class),1000);
        for(Account account : accounts2){
            System.out.println(account);
        }

        //查询一个
        List<Account> accounts1 = jt.query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),14);
        System.out.println(accounts1.get(0));

       //名称查询
List<Account> accounts = getJdbcTemplate().query("select * from account where name=?", new BeanPropertyRowMapper<Account>(Account.class), 方法参数);
        if (accounts.isEmpty()){
            return null;
        }
        if (accounts.size()>1){
            throw new RuntimeException("结果集不唯一");
        }

        Float in = jt.queryForObject("select COUNT(id)from account WHERE money>?",Float.class,1000f);
            System.out.println(in);
    }
2.JdbcDaoSupport的使用以及Dao的两种编写方式

​ 1,原始,不继承任何类,直接spring管理

需在Dao实现类添加jdbcTemplate属性,让spring管理

​ 2,继承JdbcDaoSupport这个类

不用jdbcTemplate属性,配置Dao时调用父类的set方法配置datasource,返回jdbcTemplate

3.声明式事务管理的步骤
1.xml方式

优势:一次配置,全部有效

<!-- spring中基于XML的声明式事务控制配置步骤
       1、配置事务管理器  包含操作事务的方法  提交 回滚等
       2、配置事务的通知累  
               此时我们需要导入事务的约束 tx名称空间和约束,同时也需要aop的
               使用tx:advice标签配置事务通知
                   属性:
                       id:给事务通知起一个唯一标识
                       transaction-manager:给事务通知提供一个事务管理器引用
       3、配置AOP中的通用切入点表达式
       4、建立事务通知和切入点表达式的对应关系
       5、配置事务的属性
              是在事务的通知tx:advice标签的内部

    -->
    <!-- 配置事务管理器 -->
    <bean id="transactionManager"              class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!-- 配置事务的通知类  -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
<!-- 配置事务的属性
isolation:用于指定事务的隔离级别。默认值是DEFAULT,表示使用数据库的默认隔离级别。
propagation:指定事务的传播行为。默认值是REQUIRED,一定有事务,增删改选。查可SUPPORTS。
read-only:用于指定事务是否只读。只有查询方法才能设置为true。默认值是false,表示读写。
timeout:用于指定事务的超时时间,默认值是-1,表示永不超时。如果指定了数值,以秒为单位。
rollback-for:指定一个异常该异常时回滚,其他异常时不回滚。没有默认值。表示任何异常都回滚。
no-rollback-for:指定一个异常,该异常时不回滚,其他异常回滚。没有默认值,默认都回滚。-->
            <tx:method name="*" propagation="REQUIRED" read-only="false"/>
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>         </tx:method>      
        </tx:attributes>
    </tx:advice>

    <!-- 配置aop-->
    <aop:config>
        <!-- 配置切入点表达式-->
        <aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"></aop:pointcut>
        <!--建立切入点表达式和事务通知的对应关系 -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
    </aop:config>
2.注解方式

优势:配置少

劣势:只能对当前类或方法有效

1.相比xml中少了很多配置

<!-- 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>

2.在需要事务支持的地方@Transactional注解

@Service("accountService")
//可在@Transactional中配置事务的属性,逗号分隔
@Transactional(propagation= Propagation.SUPPORTS,readOnly=true)//只读型事务的配置
public class AccountServiceImpl implements AccountService {
  
//也可在方法上配置此属性,此方法的配置优先级更高
@Transactional(propagation= Propagation.REQUIRED,readOnly=false)
public void transfer(String sourceName, String targetName, Float money) {
3.使用纯注解配置

1.创建一个spring的配置类:

//主配置类
@Configuration
@ComponentScan("com.itheima")
@Import({JdbcConfig.class,TransactionConfig.class})
@PropertySource("jdbcConfig.properties")
@EnableTransactionManagement
public class SpringConfiguration {
}

2.子配置类两个

//和连接数据库相关的配置类
public class JdbcConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;
    //创建JdbcTemplate
    @Bean(name="jdbcTemplate")
    public JdbcTemplate createJdbcTemplate(DataSource dataSource){
        return new JdbcTemplate(dataSource);
    }

   //创建数据源对象
    @Bean(name="dataSource")
    public DataSource createDataSource(){
        DriverManagerDataSource ds = new DriverManagerDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(username);
        ds.setPassword(password);
        return ds;
    }
}
//和事务相关的配置类
public class TransactionConfig {
    @Bean(name="transactionManager")
    public PlatformTransactionManager getPlatformTransactionManager(DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }
}

3.该变获取初始化容器方法

/**
 * 使用Junit单元测试:测试我们的配置
 * Spring整合junit的配置
 *      1、导入spring整合junit的jar(坐标)
 *      2、使用Junit提供的一个注解把原有的main方法替换了,替换成spring提供的
 *             @Runwith
 *      3、告知spring的运行器,spring和ioc创建是基于xml还是注解的,并且说明位置
 *          @ContextConfiguration
 *                  locations:指定xml文件的位置,加上classpath关键字,表示在类路径下
 *                  classes:指定注解类所在地位置
 *
 *   当我们使用spring 5.x版本的时候,要求junit的jar必须是4.12及以上
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class TestXmlSpringtx {

Source createDataSource(){
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
return ds;
}
}


~~~java
//和事务相关的配置类
public class TransactionConfig {
    @Bean(name="transactionManager")
    public PlatformTransactionManager getPlatformTransactionManager(DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }
}

3.该变获取初始化容器方法

/**
 * 使用Junit单元测试:测试我们的配置
 * Spring整合junit的配置
 *      1、导入spring整合junit的jar(坐标)
 *      2、使用Junit提供的一个注解把原有的main方法替换了,替换成spring提供的
 *             @Runwith
 *      3、告知spring的运行器,spring和ioc创建是基于xml还是注解的,并且说明位置
 *          @ContextConfiguration
 *                  locations:指定xml文件的位置,加上classpath关键字,表示在类路径下
 *                  classes:指定注解类所在地位置
 *
 *   当我们使用spring 5.x版本的时候,要求junit的jar必须是4.12及以上
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class TestXmlSpringtx {
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值