【Spring】Spring 学习

目录

一、什么是IOC

1.1 IOC是什么

1.2 为什么叫控制反转

1.3 依赖注入

二、Bean 控制

2.1 创建 Bean 的三种方式

2.2 Bean 的作用域

2.3 Bean 的生命周期

三、依赖注入(Dependency Injection)

3.1 能注入的数据类型

3.2 注入方式

四、使用注解操作Bean

4.1 创建 Bean 对象 @Component

4.2 使用注解注入数据 @AutoWired

4.3 使用注解改变作用范围 @Scope

4.4 修改生命周期

五、使用对象的方式实现配置类

5.1 相关知识点

5.2 使用 @ComponentScan 和 @Configuration

5.3 使用 @Import 导入子类

5.4 结合 Junit 完成单元测试

六、动态代理

6.1 动态代理的作用

6.2 通过JDK方式

6.3 使用 Cglib 配置动态代理

七、切面

7.1 切面配置和使用

八、事务

8.1 事务级别

8.2 事务传播行为


一、什么是IOC

1.1 IOC是什么

IOC 又称为控制翻转,将原本需要做的 java 反射, map 存储交给 spring 框架,从而降低各个模块中间的耦合性。

 

1.2 为什么叫控制反转

假设有 A、B、C、D 四个模块,若 A 模块需要使用到 B 模块的对象

没有IOC容器的时候:

没有 IOC 容器时,A模块需要在初始化或某个节点时 new 一个 B 对象或使用已有的 B 对象,而这个主动权在 A 模块中,A 模块能选择使用哪种 B 模块对象,是 new 一个新的,还是使用已有的

使用IOC容器后:

使用 IOC 容器后,所有的 B  对象都由 IOC 容器统一管理,即当 A 模块要使用 B 对象时,是使用 IOC 容器返回的 B 对象,这个对象的创建和管理权是在 IOC 容器手上的。

控制 -- 反转

控制:结合例子,可以发现对象的创建交给了 IOC 容器实现,即对象的创建和控制权交给了 IOC 容器,这就是Spring中的控制

反转:结合例子,未使用 IOC 容器时,对象的创建权和控制权是在他的调用对象手中的。

           使用了 IOC 容器后,对象的创建权和控制权都交给了 IOC 容器,而调用他的对象模块只能使用 IOC 容器返回的对象

 

1.3 依赖注入

DI—Dependency Injection,即“依赖注入”:是组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

依赖 -- 注入

依赖:组件依赖于 IOC 容器,通过 IOC 容器来提供对象所需要的资源,实现组件间的解耦

注入:调用组件的模块,通过向 IOC 容器获取,得到所需对象的过程,叫做注入

 

二、Bean 控制

2.1 创建 Bean 的三种方式

<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">
    <!--  将创建的对象创建给spring来管理  -->

    <!--  创建Bean的三种方式  -->
    <!--  适用默认构造函数创建
          在spring的配置文件中使用bean标签,配以id和class属性后,切没有其他属性和标签时。
          采用的就是默认构造函数创建bean对象,此时若类中没有默认构造函数,则对象无法创建-->
    <!--    <bean id="accountService" class="com.tom.www.service.impl.AccountServiceImpl"></bean>-->

    <!-- 使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器) -->
    <!--    <bean id="instanceFactory" class="com.tom.www.factory.InstanceFactory"></bean>-->
    <!--    <bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>-->

    <!--  使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)  -->
    <bean id="accountService" class="com.tom.www.factory.StaticFactory" factory-method="getAccountService"></bean>
</beans>

 

2.2 Bean 的作用域

singletom: 单例的 =》 默认 常用
prototype:多例的 => 常用
request: 作用于web应用的请求范围
session:作用于web应用的会话范围
global-session:作用于集群环境的会话范围,当不是集群环境时,它就是session
                => 一般用于集群

 

2.3 Bean 的生命周期

 

三、依赖注入(Dependency Injection)

在上面学习了如何创建一个 Bean 对象,但是若这个 Bean 对象中存在一些属性,我们要如何给他赋值呢?

给被 Ioc 控制了的 Bean 对象赋予属性值,这就叫做依赖注入。

使用依赖注入,能有效降低模块间的耦合性

 

3.1 能注入的数据类型

  • 基本类型和 String
  • 其他 bean 类型(在配置文件中或注解中配置过的 bean)
  • 复杂类型/集合类型

 

3.2 注入方式

  • 使用构造器注入(少用)
  • 使用 set 方式注入
  • 使用注解注入

 

3.2.1 使用构造器注入

<!--  构造函数注入
          使用标签:constructor-arg
          标签出现的为止: bean标签内部
          标签中的属性:
              type: 用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
              index: 用于指定要注入的数据给构造函数中指定索引位置的参数赋值,索引位置从0开始
              name(c常用): 用于指定给构造函数中指定名称的参数赋值
              ======================= 以上三个用于指定给构造函数中哪个参数赋值 =====================
              value: 用于提供基本类型和string类型的数据
              ref: 用于指定其他的bean类型数据,即在spring的ioc容器中出现过的bean对象
          优势:
              在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功
          弊端:
              改变了 Bean 对象的实例化方式,使我们在创建对象时,若用不到这些数据,也必须提供
      -->
    <bean id="accountService" class="xyz.tom.www.service.impl.AccountServiceImpl">
        <constructor-arg name="name" value="小明"></constructor-arg>
        <constructor-arg name="age" value="18"></constructor-arg>
        <constructor-arg name="birthday" ref="now"></constructor-arg>
    </bean>

    <!--  配置一个日期对象  -->
    <bean id="now" class="java.util.Date"></bean>

 

3.2.2 使用 set 方式注入

<!--  set方法注入
            设计的标签: property
            出现的为止: bean标签的内部
            标签中的属性:
              name(c常用): 用于指定注入时所调用的set方法名称
              value: 用于提供基本类型和string类型的数据
              ref: 用于指定其他的bean类型数据,即在spring的ioc容器中出现过的bean对象
            优势:
              创建对象时没有明确的限制,可以使用默认构造函数
            弊端:
              如果有某个成员必须有值,则获取对象时有可能set方法没有执行
      -->
    <bean id="accountService2" class="xyz.tom.www.service.impl.AccountServiceImpl2">
        <property name="name" value="test"></property>
        <property name="birthday" ref="now"></property>
    </bean>

    <!--  复杂类型的注入/集合类型的注入
          用于给List结构集合注入的标签: list array set
          用于给Map结构注入的标签: map props
          同类型可以互换,即只要记: list map
    -->
    <bean id="accountService3" class="xyz.tom.www.service.impl.AccountServiceImpl3">
        <property name="myStrs">
            <array>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </array>
        </property>

        <property name="myList">
            <list>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </list>
        </property>

        <property name="mySet">
            <set>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </set>
        </property>

        <property name="myMap">
            <map>
                <entry key="testA" value="aaa"></entry>
                <entry key="testB">
                    <value>BBB</value>
                </entry>
            </map>
        </property>

        <property name="myProps">
            <props>
                <prop key="testA">AAA</prop>
                <prop key="testB">BBB</prop>
                <prop key="testC">CCC</prop>
            </props>
        </property>
    </bean>

 

四、使用注解操作Bean

4.1 创建 Bean 对象 @Component

4.1.1 作用:

可以使用 @Component 来创建Bean对象

  • 作用: 用于将当前类对象存入 spring 容器中
  • 属性: value 用于指定 bean 的 id,当不写时,默认使用当前类名,且首字母改为小写

 

4.1.2 使用

创建 bean.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: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创建容器时要扫描的包,配置所需要的标签不是在beans的约束中,而是在一个名称
            为context名称空间和约束中-->
    <context:component-scan base-package="xyz.tom"></context:component-scan>

</beans>

创建对应的bean

@Component(value = "accountService")
public class AccountServiceImpl implements AccountService {

    public void saveAccount() {
        System.out.println("这是 AccountServiceImpl");
    }
}

 

4.1.3 针对三层框架的注解

根据mvc三层框架,spring分别提供了三个注解,这三个注解其实是一样的,只是使用地方有所区别:

  • @Controller:一般用在表现层
  • @Service:一般用在业务层
  • @Repository:一般用在持久层
  • @Component:其他

 

4.2 使用注解注入数据 @AutoWired

4.2.1 @Autowired 自动注入

  • 作用: 自动按照类型注入,只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
  • 位置: 可以是变量上,也可以是方法上
  • 限制: 若没有或 存在多个类型匹配时,均会报错。要求对应的bean对象类型必须唯一

 

4.2.2 @Qualifier 存在多个对象类型时,可以指定使用哪一个对象类型

如下列代码,均使用了 AccountDao,当准备注入时,发现了多个对象类型

@Repository(value = "accountDao1")
public class AccountDaoImpl implements AccountDao {
    public void saveAccount() {
        System.out.println("这是 AccountDaoImpl");
    }
}
@Repository(value = "accountDao2")
public class AccountDaoImpl2 implements AccountDao {
    public void saveAccount() {
        System.out.println("这是 AccountDaoImpl222");
    }
}

可以通过  @Qualifier 指定使用哪一个对象类型

    @Resource(name = "accountDao1")
//    @Qualifier(value = "accountDao1")
//    @Autowired
    private AccountDao accountDao = null;

 

4.2.3 使用@Resource 直接指定使用哪一个对象类型

    @Resource(name = "accountDao1")
//    @Qualifier(value = "accountDao1")
//    @Autowired
    private AccountDao accountDao = null;

 

4.2.4 使用@Value注入基本类型或string类型数据

结合SpEL使用   SpEL: ${}

 

4.3 使用注解改变作用范围 @Scope

通过 @Scope 可以设置成单例、多例等

@Service(value = "accountService")
@Scope(value = "prototype")
public class AccountServiceImpl implements AccountService {
    xxx....
}

 

4.4 修改生命周期

使用 @PreDestory 指定销毁方法

@PreDestroy
public void destroy() {
    System.out.println("destroy");
}

使用 @PostConstruct 指定初始方法

@PostConstruct
public void init() {
   System.out.println("init");
}

 

4.5 使用案例

結合dbUtil访问数据库

pom.xml:


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

        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.4</version>
        </dependency>

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

        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>

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

 

bean.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: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="xyz.tom"></context:component-scan>

    <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="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://49.233.178.108:3306/mysql?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=false"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
    </bean>
</beans>

 

查询类: accountDao:

@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {

    @Autowired
    private QueryRunner runner;

    public ArrayList<Account> findAllAccount() {
        try {
            List list = runner.query("select * from account", new BeanListHandler<Account>(Account.class));
            return (ArrayList<Account>) list;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public Account findAccountById(Integer id) {
        try {
            return runner.query("select * from account where id = ?", new BeanHandler<Account>(Account.class), id);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public void saveAccount(Account account) {
        try {
            runner.update("insert into account(name,money) values (?, ?)", account.getName(),account.getMoney());
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public void updateAccount(Account account) {
        try {
            runner.update("update account set name=?,money=? where id=?", account.getName(),account.getMoney(),account.getId());
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public void delAccount(Integer id) {
        try {
            runner.update("delete from account where id=?", id);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

 

五、使用对象的方式实现配置类

5.1 相关知识点

  • AnnotationConfigApplicationContext 加载 Bean 对象
  • @Configuration 代表 这是一个配置类对象,要被扫描到的
  • @ComponentScan 代表要扫描的包路径
  • @Import  要导入的包

 

5.2 使用 @ComponentScan 和 @Configuration

1)改用 AnnotationConfigApplicationContext 获取对象,通过参数中配置的配置类类名,表明这个类是父配置类

@Test
    public void testFindAll() {
        // 1. 获取容器
//        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
//        ApplicationContext ac = new AnnotationConfigApplicationContext(JdbcConfig.class);
        // 2. 得到业务层对象
        AccountService as = ac.getBean("accountService", AccountService.class);
        // 3. 执行方法
        ArrayList<Account> accounts = as.findAllAccount();
        for (Account account : accounts) {
            System.out.println("name = " + account.getName() + " id = " + account.getId() + " money = " + account.getMoney());
        }
    }

2)使用 @ComponentScan 和 @Configuration 实现加载子配置类

主配置类中:

// 指定扫描的路径
@ComponentScan({"xyz.tom", "config"})
public class SpringConfiguration {
}

子配置类中:

// 表明这是一个配置类,会被扫描器扫到
@Configuration
public class JdbcConfig {
....
}

 

5.3 使用 @Import 导入子类

@ComponentScan({"xyz.tom"}) // 指定父类路径
@Import(JdbcConfig.class) // 导入子类,此时子类无需加 @Configuration
public class SpringConfiguration {
   ...
}

 

5.4 结合 Junit 完成单元测试

5.4.1 使用

主配置类中

  • 导入spring整合junit的jar包
  • 使用 @RunWith 注解将原有main方法替换,改为spring提供的main方法(提供了IOC控制)
  • 告知spring运行期间,spring和ioc的创建是基于xml还是基于注解,并说明位置
  •     @ContextConfiguration: {
  •                  xml: locations: 指定xml文件的位置,加上classpath关键字,表明在类路径下
  •                  注解:classess: 指定注解类所在位置
  •      }

 

5.4.2 相关代码

pom.xml:

<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</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>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.4</version>
        </dependency>

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

        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>

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

 

测试类test.java:


import java.util.ArrayList;


/*
*   使用Junit单元测试,测试我们的配置
*   Spring整合junit的配置
*           1. 导入spring整合junit的jar
*           2. 使用Junit提供的一个注解将原有的main方法替换,改成spring提供的
*               @RunWith
*           3. 告知spring的运行期,spring和ioc创建是基于xml还是注解的,并说明位置
*               @ContextConfiguration
*                   locations: 指定xml文件的位置,加上classpath关键字,表示在类路径下
*                   classes: 指定注解类所在位置
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class AccountServiceTest{

    @Autowired
    private AccountService as = null;

//
//    @Before
//    public void init() {
//        // 1. 获取容器
//        ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
//        // 2. 得到业务层对象
//        as = ac.getBean("accountService", AccountService.class);
//    }

    @Test
    public void testFindAll() {

        // 3. 执行方法
        ArrayList<Account> accounts = as.findAllAccount();
        for (Account account : accounts) {
            System.out.println("name = " + account.getName() + " id = " + account.getId() + " money = " + account.getMoney());
        }
    }

    @Test
    public void testFindOne() {
        // 3. 执行方法
        Account account = as.findAccountById(5);
        if( account == null) {
            System.out.println("account is null");
        } else {
            System.out.println("name = " + account.getName() + " id = " + account.getId() + " money = " + account.getMoney());
        }
    }

    @Test
    public void testSave() {
        Account account = new Account();
        account.setName("test");
        account.setMoney(1005.22);

        // 3. 执行方法
        as.saveAccount(account);
    }

    @Test
    public void testUpdate() {
        Account account = new Account();
        account.setId(3);
        account.setName("testUpdate");
        account.setMoney(1999.99);

        // 3. 执行方法
        as.updateAccount(account);
    }

    @Test
    public void testDelete() {
        as.delAccount(3);
    }
}

 

主配置类:SpringConfiguration.java

//@Configuration
@ComponentScan({"xyz.tom"})
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbcConfig.properties")
public class SpringConfiguration {
}

 

jdbc配置类: jdbcConfig.java


public class JdbcConfig {

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

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

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

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

    /*
     *   用于创建一个 QueryRunner 对象
     */
    @Bean(name = "runner")
//    @Scope(value = "prototype")
    public QueryRunner createQueryRunner(DataSource datasource) {
        return new QueryRunner(datasource);
    }

    /*
     *  用于创建一个数据源对象
     */
    @Bean(name="dataSource")
    public DataSource createDataSource() {
        ComboPooledDataSource ds = new ComboPooledDataSource();
        try {
            ds.setDriverClass(dirver);
//            System.out.println(url);

//            ds.setDriverClass("com.mysql.cj.jdbc.Driver");
            ds.setJdbcUrl(url);
            ds.setUser(username);
            ds.setPassword(password);
            return ds;
        } catch (PropertyVetoException e) {
            throw new RuntimeException(e);
        }
    }
}

 

六、动态代理

6.1 动态代理的作用

通过动态代理,可以在不修改类代码的情况下,对类进行功能拓展

 

6.2 通过JDK方式

/**
        * 动态代理
        * 特点: 字节码随用随创建,随用随加载
        * 作用: 不修改源码的基础上,对方法增强
        * 分类:
        *       基于切口的动态代理
        *       基于子类的动态代理
        * 基于接口的动态代理
        *       设计的类: Proxy
        *       提供者: JDK官网
        * 如何创建代理对象:
        *       使用Proxy类中的newProxyInstance方法
        * 创建代理对象的要求:
        *       被代理类最少实现一个接口,如果没有则不能使用
        * newProxyInstance方法的参数:
        *       ClassLoader:类加载器
         *          用于加载对象字节码的,和被代理对象使用相同的类加载器,固定写法。
        *       Class[]:字节码数组
         *          用于让代理对象和被代理对象,有相同方法
        *       InvocationHandler:用于提供增强的代码
         *          它是让我们写如何代理,一般都是些该接口的实现类,通常是匿名内部类,但不是必须
         *          此接口的实现类都是谁用谁写
        */
        IProducer proxyProduer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
                producer.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     * 作用:执行被代理对象的任何接口方法都会经过盖房啊
                     * 方法参数的含义:
                     * @param proxy  代理对象的引用
                     * @param method 当前执行的方法
                     * @param args   当前执行方法所需的参数
                     * @return       和被代理对象方法有相同的返回值
                     * @throws Throwable
                     */
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 提供增强的代码
                        Object returnValue =null;
                        // 1. 获取方法执行的参数
                        Float money = (Float) args[0];
                        // 2. 判断当前方式是不是销售
                        if("saleProduct".equals(method.getName())) {
                            returnValue = method.invoke(producer, money*0.8f);
                        }
                        return returnValue;
                    }
                });

        proxyProduer.saleProduct(10000f);

 

6.3 使用 Cglib 配置动态代理

final Producer producer = new Producer();

        /**
        * 动态代理
        * 特点: 字节码随用随创建,随用随加载
        * 作用: 不修改源码的基础上,对方法增强
        * 分类:
        *       基于切口的动态代理
        *       基于子类的动态代理
        * 基于子类的动态代理
        *       设计的类: Enhancer
        *       提供者: 第三方cglib库
        * 如何创建代理对象:
        *       使用Enhancer类中的create方法
        * 创建代理对象的要求:
        *       被代理类不能是最终类
        * create方法的参数:
        *       Class:字节码
         *          用于指定被代理对象的字节码。
        *       Callback:用于提供增强的代码
        */
        IProducer cglibProxyProduer = (IProducer) Enhancer.create(producer.getClass(), new MethodInterceptor() {

            /**
             * 执行对象的任何方法一定会经过该方法
             * @param proxy
             * @param method
             * @param args
             *      以上三个参数和基于接口的动态代理中invoke方法中的参数是一样的
             * @param methodProxy // 当前执行方法的代理对象
             * @return
             * @throws Throwable
             */
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                // 提供增强的代码
                Object returnValue =null;
                // 1. 获取方法执行的参数
                Float money = (Float) args[0];
                // 2. 判断当前方式是不是销售
                if("saleProduct".equals(method.getName())) {
                    returnValue = method.invoke(producer, money*0.8f);
                }
                return returnValue;
            }
        });

        cglibProxyProduer.saleProduct(12000f);

 

七、切面

7.1 切面配置和使用

7.1.1 切入点表达式写法 

关键字:execution(表达式)

标准的表达式写法:

       public void xyz.tom.www.service.impl.AccountServiceImpl.saveAccount()

1)访问修饰符可以省略

      void xyz.tom.www.service.impl.AccountServiceImpl.saveAccount()

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

      * xyz.tom.www.service.impl.AccountServiceImpl.saveAccount()

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

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

4)使用.. 表明当前包及子包

      * *..AccountServiceImpl.saveAccount()

5)类名和方法名都可以使用*做通配

       * *..*.*()

6)参数列表可以直接写类型:

           基本类型写名称: int

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

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

7)可以用..表示有无参数均可,有参数可以是任意类型

           全通配写法: * *..*.*(...)

实际开发中切入点表达式的通常写法: 切到业务层实现类的所有方法

            * xyz.tom.www.service.impl.*.*(..)

 

7.1.2 使用样例

<bean id="logger" class="xyz.tom.www.utils.Logger"></bean>
    <aop:config>
        <aop:aspect id="logAdvice" ref="logger">
<!--            <aop:before method="printLog" pointcut="execution( public void xyz.tom.www.service.impl.AccountServiceImpl.saveAccount())"></aop:before>-->
            <aop:before method="printLog" pointcut="execution( * *..*.*(..))"></aop:before>
<!--            <aop:before method="printLog" pointcut="execution( * *..AccountServiceImpl.saveAccount())"></aop:before>-->
        </aop:aspect>
    </aop:config>

 

八、事务

8.1 事务级别

事务隔离级别反映事务提交并发询问时的处理态度

事务级别说明
ISOLATION_READ_UNCOMMITTED可以读取未提交的数据
ISOLATION_READ_COMMITTED只能读取已提交的数据,解决脏读问题(Oracle的默认级别)
ISOLATION_REPEATABLE_READ是否读取其他事务提交修改后的数据,解决不可重复读问题(MySQL默认级别)
ISOLATION_SERIALIZABLE是否读取其他事务提交添加后的数据,解决幻影读的问题

 

8.2 事务传播行为

事务传播行为说明使用场景
PROPAGATION_REQUIRED支持当前事务,假设当前没有事务。就新建一个事务增删修
PROPAGATION_SUPPORTS支持当前事务,假设当前没有事务,就以非事务方式运行查询
PROPAGATION_MANDATORY支持当前事务,假设当前没有事务,就抛出异常 
PROPAGATION_REQUIRES_NEW新建事务,假设当前存在事务。把当前事务挂起 
PROPAGATION_NOT_SUPPORTED以非事务方式运行操作。假设当前存在事务,就把当前事务挂起 
PROPAGATION_NEVER

以非事务方式运行,假设当前存在事务,则抛出异常

 
PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值