03 Spring
lix_uan 29 0

IOC入门案例

  • 导入依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.10.RELEASE</version>
        </dependency>
    
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
  • 定义BookDao接口和BookDaoImpl实现类

    public interface BookDao {
        public void save();
    }
    
    public class BookDaoImpl implements BookDao {
        public void save() {
            System.out.println("book dao save ...");
        }
    }
  • 定义BookService接口和BookServiceImpl实现类

    public interface BookService {
        public void save();
    }
    
    public class BookServiceImpl implements BookService {
        private BookDao bookDao = new BookDaoImpl();
        public void save() {
            System.out.println("book service save ...");
            bookDao.save();
        }
    }
  • 定义applicationContext.xml配置文件并配置BookServiceImpl

    <?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">
    
        <!--
            bean标签:表示配置bean
            id属性:表示给bean起名字
            class属性:表示给bean定义类型
        -->
        <bean id="bookService" class="cn.lixuan.service.impl.BookServiceImpl"></bean>
    
    </beans>
  • 初始化IOC容器(Spring核心容器/Spring容器),通过容器获取Bean对象

    public class App {
        public static void main(String[] args) {
            //1.创建IoC容器对象,加载spring核心配置文件
            ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
            //2 从IOC容器中获取Bean对象(BookService对象)
            BookService bookService= (BookService)ctx.getBean("bookService");
            //3 调用Bean对象(BookService对象)的方法
            bookService.save();
        }
    }

DI入门案例

  • BookServiceImpl中删除使用new的形式创建对象的代码

    public class BookServiceImpl implements BookService {
        private BookDao bookDao;  //删除使用new的形式创建对象的代码
        public void save() {
            System.out.println("book service save ...");
            bookDao.save();
        }
    }
  • 提供依赖对象对应的setter方法

    public class BookServiceImpl implements BookService {
        private BookDao bookDao;
        public void save() {
            System.out.println("book service save ...");
            bookDao.save();
        }
        //提供依赖对象对应的setter方法
        public void setBookDao(BookDao bookDao) {
            this.bookDao = bookDao;
        }
    }
  • 在applicationContext.xml中配置service与dao之间的关系

    <?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">
        <!--
            bean标签:表示配置bean
            id属性:表示给bean起名字
            class属性:表示给bean定义类型
        -->
        <bean id="bookDao" class="cn.lixuan.dao.impl.BookDaoImpl"/>
    
        <bean id="bookService" class="cn.lixuan.service.impl.BookServiceImpl">
            <!--配置server与dao的关系
                property标签:表示配置当前bean的属性
                name属性:表示配置哪一个具体的属性
                ref属性:表示参照哪一个bean
            -->
            <property name="bookDao" ref="bookDao"/>
        </bean>
    </beans>

Bean的配置

别名配置

 <bean id="bookService" name="service bookEbi" class="cn.lixuan.service.impl.BookServiceImpl">

作用范围配置

<!-- prototype,  singleton等,默认为单例-->
<bean id="bookService" scope="singleton" class="cn.lixuan.service.impl.BookServiceImpl">

Bean的实例化

构造方法方式

  • BookDaoImpl实现类

    public class BookDaoImpl implements BookDao {
    
        // 构造方法
        public BookDaoImpl() {
            System.out.println("book dao constructor is running ....");
        }
        public void save() {
            System.out.println("book dao save ...");
        }
    }
  • AppForInstanceBook测试类

    public void AppForInstanceBook() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
    
        bookDao.save();
    }

静态工厂方式

  • OrderDao接口和OrderDaoImpl实现类

    public interface OrderDao {
        public void save();
    }
    public class OrderDaoImpl implements OrderDao {
        public void save() {
            System.out.println("order dao save ...");
        }
    }
  • OrderDaoFatory工厂类

    //静态工厂创建对象
    public class OrderDaoFactory {
        // static
        public static OrderDao getOrderDao(){
            System.out.println("factory setup....");
            return new OrderDaoImpl();
        }
    }
  • applicationContext.xml配置

    <bean id="orderDao" class="cn.lixuan.factory.OrderDaoFactory" factory-method="getOrderDao"/>
  • AppForInstanceOrder测试类

    public void AppForInstanceOrder() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    
        OrderDao orderDao = (OrderDao) ctx.getBean("orderDao");
    
        orderDao.save();
    }

实例工厂方式

  • UserDaoFactory工厂类

    //实例工厂创建对象
    public class UserDaoFactory {
        public UserDao getUserDao(){
            return new UserDaoImpl();
        }
    }
  • applicationContext.xml配置

    <bean id="userFactory" class="cn.lixuan.factory.UserDaoFactory"/>
    
    <bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>
  • AppForInstanceUser测试类

    public void AppForInstanceUser() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao = (UserDao) ctx.getBean("userDao");
        userDao.save();
    }

Bean的生命周期

  • 方式一 提供生命周期控制方法

    public class BookDaoImpl implements BookDao {
        public void save() {
            System.out.println("book dao save ...");
        }
        //表示bean初始化对应的操作
        public void init(){
            System.out.println("init...");
        }
        //表示bean销毁前对应的操作
        public void destory(){
            System.out.println("destory...");
        }
    }
  • 方式二 实现InitializingBean, DisposableBean接口

    public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {
        private BookDao bookDao;
        public void setBookDao(BookDao bookDao) {
            System.out.println("set .....");
            this.bookDao = bookDao;
        }
        public void save() {
            System.out.println("book service save ...");
            bookDao.save();
        }
        public void destroy() throws Exception {
            System.out.println("service destroy");
        }
        public void afterPropertiesSet() throws Exception {
            System.out.println("service init");
        }
    }
  • applicationContext.xml配置

    <!--init-method:设置bean初始化生命周期回调函数,此处填写init方法名-->
    <!--destroy-method:设置bean销毁生命周期回调函数,仅适用于单例对象,此处填写destory方法名-->
    <bean id="bookDao" class="cn.lixuan.dao.impl.BookDaoImpl" init-method="init" destroy-method="destory"/>
  • 测试类

    public void AppForLifeCycle() {
        //此处需要使用ClassPathXmlApplicationContext,ApplicationContext没有close方法
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        bookDao.save();
    
        //注册关闭钩子函数,在虚拟机退出之前回调此函数,关闭容器
        //ctx.registerShutdownHook();
        //关闭容器,执行销毁的方法
        ctx.close();
    
    }

依赖注入

依赖注入的两种方式

  • setter注入, 构造器注入

    <bean id="bookDao" class="cn.lixuan.dao.impl.BookDaoImpl"/>
    
    <!-- setter引用类型注入 -->
    <bean id="bookService" class="cn.lixuan.service.impl.BookServiceImpl">
        <property name="bookDao" ref="bookDao"/>
    </bean>
    
    <!-- setter简单类型注入 -->
    <bean id="bookService" class="cn.lixuan.service.impl.BookServiceImpl">
        <property name="bookDao" value="20"/>
    </bean>
    
    <!-- 构造方式注入,需提供对应的构造方法 -->
    <bean id="bookService" class="cn.lixuan.service.impl.BookServiceImpl">
        <constructor-arg name="bookDao" ref="bookDao"/>
    </bean>

按类型自动装配

<bean id="bookDao" class="cn.lixuan.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="cn.lixuan.service.impl.BookServiceImpl" autowire="byType"/>

集合注入

  • 注入数组类型数据

    <property name="array">
        <array>
            <value>100</value>
            <value>200</value>
            <value>300</value>
        </array>
    </property>
  • 注入List类型数据

    <property name="list">
        <list>
            <value>itcast</value>
            <value>itheima</value>
            <value>boxuegu</value>
            <value>chuanzhihui</value>
        </list>
    </property>
  • 注入Set类型数据

    <property name="set">
        <set>
            <value>itcast</value>
            <value>itheima</value>
            <value>boxuegu</value>
            <value>boxuegu</value>
        </set>
    </property>
  • 注入Map类型数据

    <property name="map">
        <map>
            <entry key="country" value="china"/>
            <entry key="province" value="henan"/>
            <entry key="city" value="kaifeng"/>
        </map>
    </property>
  • 注入Properties类型数据

    <property name="properties">
        <props>
            <prop key="country">china</prop>
            <prop key="province">henan</prop>
            <prop key="city">kaifeng</prop>
        </props>
    </property>

第三方资源配置管理

管理Druid连接池

  • 数据库准备

    create database if not exists spring_db character set utf8;
    use spring_db;
    create table if not exists tbl_account(
        id int primary key auto_increment,
        name varchar(20),
        money double
    );
    insert into tbl_account values(null,'Tom',1000);
    insert into tbl_account values(null,'Jerry',1000);
  • 添加依赖

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.16</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
  • 配置DruidDataSource连接池Bean对象

    <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/spring_db"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
  • 在测试类中从IOC容器中获取连接池对象并打印

    public void duridTest)() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        DataSource dataSource = (DataSource) ctx.getBean("dataSource");
        System.out.println(dataSource);
    }

管理C3P0连接池

  • 添加c3p0连接池依赖

    <dependency>
        <groupId>c3p0</groupId>
        <artifactId>c3p0</artifactId>
        <version>0.9.1.2</version>
    </dependency>
  • 配置c3p0连接池Bean对象

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring_db"/>
        <property name="user" value="root"/>
        <property name="password" value="root"/>
        <property name="maxPoolSize" value="1000"/>
    </bean>
  • 在测试类中从IOC容器中获取连接池对象并打印

    public void c3p0Test {
        public static void main(String[] args) {
            ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
            DataSource dataSource = (DataSource) ctx.getBean("dataSource");
            System.out.println(dataSource);
        }
    }

加载properties属性文件

  • 编写jdbc.properties属性文件

    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db
    jdbc.username=root
    jdbc.password=root
  • 在applicationContext.xml中开启开启context命名空间,加载jdbc.properties属性文件

    <?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
                ">
        <!-- 加载多个properties文件 -->
        <!-- <context:property-placeholder-location="jdbc.properties,msg.properties"/> -->
    
        <context:property-placeholder location="classpath*:*.properties" system-properties-mode="NEVER"/>
    
        <bean class="com.alibaba.druid.pool.DruidDataSource">
            <property name="driverClassName" value="${jdbc.driver}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>
    
        <bean id="bookDao" class="cn.lixuan.dao.impl.BookDaoImpl">
            <property name="name" value="${username}"/>
        </bean>
    
    </beans>

Spring容器

  • 加载多个配置文件

    ApplicationContext ctx = new ClassPathXmlApplicationContext("bean1.xml", "bean2.xml");

获取Bean对象

  • 使用bean名称获取(需要自己强制类型转换)

    BookDao bookDao = (BookDao) ctx.getBean("bookDao");
  • 使用bean名称获取并指定类型

    BookDao bookDao = ctx.getBean("bookDao", BookDao.class);
  • 使用bean类型获取(如果IOC容器中同类型的Bean对象有多个,此处获取会报错)

    BookDao bookDao = ctx.getBean(BookDao.class);

Spring注解开发

  • @Component注解的三个衍生注解

    • @Controller:用于表现层bean定义
    • @Service:用于业务层bean定义
    • @Repository:用于数据层bean定义
  • 定义配置类代替配置文件

    //声明当前类为Spring配置类
    @Configuration
    @ComponentScan("cn.lixuan")
    //设置bean扫描路径,多个路径书写为字符串数组格式
    //@ComponentScan({"cn.lixuan.service","cn.lixuan.dao"})
    public class SpringConfig {
    }
  • 在测试类中加载配置类,获取Bean对象并使用

    public void AppForAnnotation() {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        System.out.println(bookDao);
    
        BookService bookService = ctx.getBean(BookService.class);
        System.out.println(bookService);
    
    }

作用范围与生命周期

@Repository
@Scope("singleton")
public class BookDaoImpl implements BookDao {
    public BookDaoImpl() {
        System.out.println("book dao constructor ...");
    }
    @PostConstruct
    public void init(){
        System.out.println("book init ...");
    }
    @PreDestroy
    public void destroy(){
        System.out.println("book destory ...");
    }
}
<!-- JDK9后使用@PostConstruct和@PreDestroy要导入依赖 -->
<dependency>
  <groupId>javax.annotation</groupId>
  <artifactId>javax.annotation-api</artifactId>
  <version>1.3.2</version>
</dependency>

自动装配

@Service
public class BookServiceImpl implements BookService {
    //@Autowired:注入引用类型,自动装配模式,默认按类型装配
    @Autowired
    //@Qualifier:自动装配bean时按bean名称装配
    //@Qualifier("bookDao")
    private BookDao bookDao;

    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
}

使用@Value实现简单类型注入

@Repository("bookDao")
public class BookDaoImpl implements BookDao {
    //@Value:注入简单类型(无需提供set方法)
    @Value("${name}")
    private String name;

    public void save() {
        System.out.println("book dao save ..." + name);
    }
}
// 以上@Value注解中使用${name}从属性文件中读取name值,那么就需要在配置类或者配置文件中加载属性文件

@Configuration
@ComponentScan("cn.lixuan")
//@PropertySource加载properties配置文件
@PropertySource({"classpath:jdbc.properties"}) //{}可以省略不写
public class SpringConfig {
}

注解开发管理第三方Bean

  • 单独定义配置类

    public class JdbcConfig {
        //@Bean:表示当前方法的返回值是一个bean对象,添加到IOC容器中
        @Bean
        public DataSource dataSource(){
            DruidDataSource ds = new DruidDataSource();
            ds.setDriverClassName("com.mysql.jdbc.Driver");
            ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
            ds.setUsername("root");
            ds.setPassword("root");
            return ds;
        }
    }
  • 将独立的配置类加入核心配置

    @Configuration
    @ComponentScan("cn.lixuan")
    //@Import:导入配置信息
    @Import({JdbcConfig.class})
    
    //@ComponentScan({"cn.lixuan.config","cn.lixuan.service","cn.lixuan.dao"})  //只要cn.lixuan.config包扫到了就行,三个包可以合并写成cn.lixuan
    public class SpringConfig {
    }

注解开发为第三方Bean注入资源

public class JdbcConfig {
    //1.定义一个方法获得要管理的对象
    @Value("com.mysql.jdbc.Driver")
    private String driver;
    @Value("jdbc:mysql://localhost:3306/spring_db")
    private String url;
    @Value("root")
    private String userName;
    @Value("root")
    private String password;
    //2.@Bean:表示当前方法的返回值是一个bean对象,添加到IOC容器中
    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }
}

引用类型依赖注入

//Spring会自动从IOC容器中找到BookDao对象赋值给参数bookDao变量,如果没有就会报错。
@Bean 
public DataSource dataSource(BookDao bookDao){
    System.out.println(bookDao);
    DruidDataSource ds = new DruidDataSource();
    ds.setDriverClassName(driver);
    ds.setUrl(url);
    ds.setUsername(userName);
    ds.setPassword(password);
    return ds;
}

Spring整合

Spring整合Mybatis

  • 添加依赖

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.10.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.16</version>
    </dependency>
    
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.6</version>
    </dependency>
    
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
    
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.2.10.RELEASE</version>
    </dependency>
    
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>1.3.0</version>
    </dependency>
  • 准备service和dao层基础代码

    public interface AccountService {
    
        void save(Account account);
    
        void delete(Integer id);
    
        void update(Account account);
    
        List<Account> findAll();
    
        Account findById(Integer id);
    }
    @Service
    public class AccountServiceImpl implements AccountService {
    
        @Autowired
        private AccountDao accountDao;
    
        public void save(Account account) {
            accountDao.save(account);
        }
    
        public void update(Account account){
            accountDao.update(account);
        }
    
        public void delete(Integer id) {
            accountDao.delete(id);
        }
    
        public Account findById(Integer id) {
            return accountDao.findById(id);
        }
    
        public List<Account> findAll() {
            return accountDao.findAll();
        }
    }
    public interface AccountDao {
    
        @Insert("insert into tbl_account(name,money)values(#{name},#{money})")
        void save(Account account);
    
        @Delete("delete from tbl_account where id = #{id} ")
        void delete(Integer id);
    
        @Update("update tbl_account set name = #{name} , money = #{money} where id = #{id} ")
        void update(Account account);
    
        @Select("select * from tbl_account")
        List<Account> findAll();
    
        @Select("select * from tbl_account where id = #{id} ")
        Account findById(Integer id);
    }
  • 创建JdbcConfig配置DataSource数据源

    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/spring_db?useSSL=false
    jdbc.username=root
    jdbc.password=root
    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
        public DataSource dataSource(){
            DruidDataSource ds = new DruidDataSource();
            ds.setDriverClassName(driver);
            ds.setUrl(url);
            ds.setUsername(userName);
            ds.setPassword(password);
            return ds;
        }
    }
  • 创建MybatisConfig整合mybatis

    public class MybatisConfig {
        //定义bean,SqlSessionFactoryBean,用于产生SqlSessionFactory对象
        @Bean
        public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
            SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
            ssfb.setTypeAliasesPackage("cn.lixuan.domain");
            ssfb.setDataSource(dataSource);
            return ssfb;
        }
        //定义bean,返回MapperScannerConfigurer对象
        @Bean
        public MapperScannerConfigurer mapperScannerConfigurer(){
            MapperScannerConfigurer msc = new MapperScannerConfigurer();
            msc.setBasePackage("cn.lixuan.dao");
            return msc;
        }
    }
  • 创建SpringConfig主配置类进行包扫描和加载其他配置类

    @Configuration
    @ComponentScan("cn.lixuan")
    //@PropertySource:加载类路径jdbc.properties文件
    @PropertySource("classpath:jdbc.properties")
    @Import({JdbcConfig.class,MybatisConfig.class})
    public class SpringConfig {
    }
  • 定义测试类进行测试

    public void springMyBatis() {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
    
        AccountService accountService = ctx.getBean(AccountService.class);
    
        Account ac = accountService.findById(1);
        System.out.println(ac);
    }

Spring整合Junit

  • 导入依赖

    <!--junit-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
    </dependency>
    <!--spring整合junit-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>5.1.9.RELEASE</version>
    </dependency>
  • 加载配置文件或者配置类

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = {SpringConfig.class})
    public class AccountServiceTest {
        //支持自动装配注入bean
        @Autowired
        private AccountService accountService;
    
        @Test
        public void testFindById(){
            System.out.println(accountService.findById(1));
        }
    
        @Test
        public void testFindAll(){
            System.out.println(accountService.findAll());
        }
    }

AOP

  • 导入aop相关坐标

    <dependencies>
        <!--spring核心依赖,会将spring-aop传递进来-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.10.RELEASE</version>
        </dependency>
        <!--切入点表达式依赖,目的是找到切入点方法,也就是找到要增强的方法-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>
    </dependencies>
  • 定义dao接口与实现类

    public interface BookDao {
        public void save();
        public void update();
    }
    @Repository
    public class BookDaoImpl implements BookDao {
    
        public void save() {
            System.out.println(System.currentTimeMillis());
            System.out.println("book dao save ...");
        }
        public void update(){
            System.out.println("book dao update ...");
        }
    }
  • 定义通知类,制作通知方法

    //通知类必须配置成Spring管理的bean
    @Component
    public class MyAdvice {
        public void method(){
            System.out.println(System.currentTimeMillis());
        }
    }
  • 定义切入点表达式、配置切面(绑定切入点与通知关系)

    //通知类必须配置成Spring管理的bean
    @Component
    //设置当前类为切面类类
    @Aspect
    public class MyAdvice {
        //设置切入点,@Pointcut注解要求配置在方法上方
        @Pointcut("execution(void cn.lixuan.dao.BookDao.update())")
        private void pt(){}
    
        //设置在切入点pt()的前面运行当前操作(前置通知)
        @Before("pt()")
        public void method(){
            System.out.println(System.currentTimeMillis());
        }
    }
  • 在配置类中进行Spring注解包扫描和开启AOP功能

    @Configuration
    @ComponentScan("cn.lixuan")
    //开启注解开发AOP功能
    @EnableAspectJAutoProxy
    public class SpringConfig {
    }
  • 测试类和运行结果

    public void testAOP() {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookDao bookDao = ctx.getBean(BookDao.class);
        bookDao.update();
    }

切入点表达式

// *  :单个独立的任意符号
// .. : 多个连续的任意符号
// +  : 专用于匹配子类类型

execution(void cn.lixuan.dao.BookDao.update())

通知类型

  • 前置通知

  • 后置通知

  • 返回后通知:@AfterReturning

  • 抛出异常后通知:@AfterThrowing

  • 环绕通知:@Around

    // 环绕通知方法的返回值建议写成Object类型, 用于将原始对象方法的返回值进行返回
    
    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("around before advice ...");
        Object ret = pjp.proceed();
        System.out.println("around after advice ...");
        return ret;
    }

AOP案例

测量业务层接口万次执行效率

  • 编写通知类

    @Component
    @Aspect
    public class ProjectAdvice {
        //匹配业务层的所有方法
        @Pointcut("execution(* cn.lixuan.service.*Service.*(..))")
        private void servicePt(){}
    
        //设置环绕通知,在原始操作的运行前后记录执行时间
        @Around("servicePt()")
        public void runSpeed(ProceedingJoinPoint pjp) throws Throwable {
            //获取执行的签名对象
            Signature signature = pjp.getSignature();
            //获取接口/类全限定名
            String className = signature.getDeclaringTypeName();
            //获取方法名
            String methodName = signature.getName();
            //记录开始时间
            long start = System.currentTimeMillis();
            //执行万次操作
            for (int i = 0; i < 10000; i++) {
               pjp.proceed();
            }
            //记录结束时间
            long end = System.currentTimeMillis();
            //打印执行结果
            System.out.println("万次执行:"+ className+"."+methodName+"---->" +(end-start) + "ms");
        }
    }
  • 在SpringConfig配置类上开启AOP注解功能

    @Configuration
    @ComponentScan("cn.lixuan")
    @PropertySource("classpath:jdbc.properties")
    @Import({JdbcConfig.class,MybatisConfig.class})
    @EnableAspectJAutoProxy //开启AOP注解功能
    public class SpringConfig {
    }
  • 运行测试类,查看结果

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = SpringConfig.class)
    public class AccountServiceTestCase {
        @Autowired
        private AccountService accountService;
        @Test
        public void testFindById(){
            Account account = accountService.findById(2);
        }
        @Test
        public void testFindAll(){
            List<Account> list = accountService.findAll();
        }
    }

AOP切入点数据获取

  • 获取参数

    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        Object[] args = pjp.getArgs(); //获取连接点方法的参数们
        System.out.println(Arrays.toString(args));
        Object ret = pjp.proceed();
        return ret;
    }
  • 抛出异常后通知可以获取切入点方法中出现的异常信息,使用形参可以接收对应的异常对象

    @AfterThrowing(value = "pt()",throwing = "t")
    public void afterReturning(Throwable t) { //变量名要和returning="t"的属性值一致
        System.out.println("afterReturning advice ..."+t);
    }
    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp)  {
        Object ret = null;
        //此处需要try...catch处理,catch中捕获到的异常就是连接点方法中抛出的异常
        try {
            ret = pjp.proceed();
        } catch (Throwable t) {
            t.printStackTrace();
        }
        return ret;
    }
  • 环绕通知中可以手工书写对原始方法的调用,得到的结果即为原始方法的返回值

    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        // 手动调用连接点方法,返回值就是连接点方法的返回值
        Object ret = pjp.proceed();
        return ret;
    }

百度网盘密码数据兼容处理

  • service层

    public interface ResourcesService {
        public boolean openURL(String url ,String password);
    }
    @Service
    public class ResourcesServiceImpl implements ResourcesService {
        @Autowired
        private ResourcesDao resourcesDao;
    
        public boolean openURL(String url, String password) {
            return resourcesDao.readResources(url,password);
        }
    }
  • dao层

    public interface ResourcesDao {
        boolean readResources(String url, String password);
    }
    @Repository
    public class ResourcesDaoImpl implements ResourcesDao {
        public boolean readResources(String url, String password) {
            System.out.println(password.length());
            //模拟校验
            return password.equals("root");
        }
    }
  • 编写通知类

    @Component
    @Aspect
    public class DataAdvice {
    
        @Pointcut("execution(boolean cn.lixuan.service.*Service.*(*,*))")
        private void servicePt(){}
    
        @Around("DataAdvice.servicePt()")
        public Object trimStr(ProceedingJoinPoint pjp) throws Throwable {
            Object[] args = pjp.getArgs();
            for (int i = 0; i < args.length; i++) {
                //判断参数是不是字符串
                if(args[i].getClass().equals(String.class)){
                    args[i] = args[i].toString().trim();
                }
            }
            Object ret = pjp.proceed(args);
            return ret;
        }
    }
  • 在SpringConfig配置类上开启AOP注解功能

    @Configuration
    @ComponentScan("cn.lixuan")
    @EnableAspectJAutoProxy
    public class SpringConfig {
    }
  • 运行测试类,查看结果

    public void testBaidu() {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        ResourcesService resourcesService = ctx.getBean(ResourcesService.class);
        boolean flag = resourcesService.openURL("http://pan.baidu.com/haha", "root ");
        System.out.println(flag);
    }

Spring事物管理

  • dao层

    public interface AccountDao {
    
        @Update("update tbl_account set money = money + #{money} where name = #{name}")
        void inMoney(@Param("name") String name, @Param("money") Double money);
    
        @Update("update tbl_account set money = money - #{money} where name = #{name}")
        void outMoney(@Param("name") String name, @Param("money") Double money);
    }
  • service层

    public interface AccountService {
        /**
         * 转账操作
         * @param out 传出方
         * @param in 转入方
         * @param money 金额
         */
    
        //配置当前接口方法具有事务
        @Transactional
        public void transfer(String out,String in ,Double money) ;
    }
    
    @Service
    public class AccountServiceImpl implements AccountService {
        @Autowired
        private AccountDao accountDao;
    
        public void transfer(String out,String in ,Double money) {
            accountDao.outMoney(out,money);
            int i = 1/0;
            accountDao.inMoney(in,money);
        }
    }
  • 在JdbcConfig中配置事务管理器

    //配置事务管理器,mybatis使用的是jdbc事务
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource){
        DataSourceTransactionManager dtm = new DataSourceTransactionManager();
        dtm.setDataSource(dataSource);
        return dtm;
    }
  • 开启注解式事务驱动

    @Configuration
    @ComponentScan("cn.lixuan")
    @PropertySource("classpath:jdbc.properties")
    @Import({JdbcConfig.class,MybatisConfig.class})
    //开启注解式事务驱动
    @EnableTransactionManagement
    public class SpringConfig {
    }
  • 运行测试类,查看结果

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = SpringConfig.class)
    public class AccountServiceTest {
    
        @Autowired
        private AccountService accountService;
    
        @Test
        public void testTransfer() throws IOException {
            accountService.transfer("Tom","Jerry",100D);
        }
    }

案例:转账业务追加日志

  • 创建日志表

    USE spring_db;
    CREATE TABLE tbl_log(
        id INT PRIMARY KEY AUTO_INCREMENT,
        info VARCHAR(255),
        createDate DATE
    );
  • 准备环境

    public interface LogService {
        //propagation设置事务属性:传播行为设置为当前操作需要新事务
        @Transactional
        void log(String out, String in, Double money);
    }
    
    @Service
    public class LogServiceImpl implements LogService {
    
        @Autowired
        private LogDao logDao;
        public void log(String out,String in,Double money ) {
            logDao.log("转账操作由"+out+"到"+in+",金额:"+money);
        }
    }
    
    public interface LogDao {
        @Insert("insert into tbl_log (info,createDate) values(#{info},now())")
        void log(String info);
    }
  • 在AccountServiceImpl中调用logService中添加日志的方法

    @Service
    public class AccountServiceImpl implements AccountService {
        @Autowired
        private AccountDao accountDao;
    
        @Autowired
        private LogService logService;
    
        public void transfer(String out,String in ,Double money) {
            try{
                accountDao.outMoney(out,money);
                int i = 1/0;
                accountDao.inMoney(in,money);
            }finally {
                logService.log(out,in,money);
            }
        }
    }
  • 在LogService的log()方法上设置事务的传播行为

    public interface LogService {
        //propagation设置事务属性:传播行为设置为当前操作需要新事务
        @Transactional(propagation = Propagation.REQUIRES_NEW)
        void log(String out, String in, Double money);
    }
  • 运行测试类,查看结果

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = SpringConfig.class)
    public class AccountServiceTest {
        @Autowired
        private AccountService accountService;
    
        @Test
        public void testTransfer() throws IOException {
            accountService.transfer("Tom","Jerry",50D);
        }
    }
评论区

索引目录