spring

一、IoC入门

1.pom.xml 配置

pom.xml 添加依赖

  <dependencies>

<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-contextartifactId>
    <version>6.0.9version>
dependency>
  dependencies>

2.配置bean

spring\src\main\resources\applicationContext.xml



<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 id="bookDao" class="com.mercurows.dao.impl.BookDaoImpl">bean>
    <bean id="bookService" class="com.mercurows.service.impl.BookServiceImpl">bean>

beans>

3.实体类

spring\src\main\java\com\mercurows\dao\BookDao.java

package com.mercurows.dao;

public interface BookDao{
    public void save();
}

spring\src\main\java\com\mercurows\dao\impl\BookDaoImpl.java

package com.mercurows.dao.impl;

import com.mercurows.dao.BookDao;

public class BookDaoImpl implements BookDao{
    public void save() {
        System.out.println("Book dao save");
    }
}

spring\src\main\java\com\mercurows\service\BookService.java

package com.mercurows.service;

public interface BookService {
    public void save();
}

spring\src\main\java\com\mercurows\service\impl\BookServiceImpl.java

package com.mercurows.service.impl;

import com.mercurows.dao.BookDao;
import com.mercurows.dao.impl.BookDaoImpl;
import com.mercurows.service.BookService;

public class BookServiceImpl implements BookService{
    private BookDao bookDao = new BookDaoImpl();

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

4.测试

spring\src\main\java\com\mercurows\App2.java

package com.mercurows;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.mercurows.dao.BookDao;
import com.mercurows.service.BookService;

public class App2
{
    public static void main( String[] args )
    {
        // 3.获取IoC容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        // 4. 获取bean
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        bookDao.save();

        BookService bookService = (BookService) ctx.getBean("bookService");
        bookService.save();
    }
}

5.项目结构图

spring_第1张图片


二、DI入门

spring_第2张图片

1.删除使用new的形式创建对象的代码

对应下面代码中的 注解 5. 5. 5.

2. 提供依赖对象对应的setter方法

对应下面代码中的 注解 6. 6. 6.

且该方法由容器调用

修改文件spring\src\main\java\com\mercurows\service\impl\BookServiceImpl.java

package com.mercurows.service.impl;

import com.mercurows.dao.BookDao;
import com.mercurows.service.BookService;

public class BookServiceImpl implements BookService {
    // 5.删除业务层中使用new的方式创建的dao对象
    private BookDao bookDao;

    public void save() {
        System.out.println("Book service save ");
        bookDao.save();
    }
    // 6.提供对应的set方法
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
}

3.配置service与dao之间的关系

对应下面代码中的 注解 7. 7. 7.

修改文件spring\src\main\resources\applicationContext.xml



<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 id="bookDao1" class="com.mercurows.dao.impl.BookDaoImpl"/>
    <bean id="bookService" class="com.mercurows.service.impl.BookServiceImpl">
        
        

        
        <property name= "bookDao" ref="bookDao1"/>
    bean>
beans>

其余的部分与IoC中一致

4.测试

spring\src\main\java\com\mercurows\App2.java

package com.mercurows;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.mercurows.dao.BookDao;
import com.mercurows.service.BookService;

public class App2
{
    public static void main( String[] args )
    {
        // 3.获取IoC容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        // 4. 获取bean
        BookDao bookDao = (BookDao) ctx.getBean("bookDao1");
        bookDao.save();

        BookService bookService = (BookService) ctx.getBean("bookService");
        bookService.save();
    }
}

附:bean基础配置

spring_第3张图片

spring_第4张图片

spring_第5张图片

实例化bean

方法一:构造方法

见上面

方法二:使用静态工厂

spring\src\main\java\com\mercurows\dao\impl\CustomerDaoImpl.java

package com.mercurows.dao.impl;

import com.mercurows.dao.CustmoerDao;

public class CustomerDaoImpl implements CustmoerDao{
    public void save() {
        System.out.println("customer dao save");
    }
}

spring\src\main\java\com\mercurows\dao\CustmoerDao.java

package com.mercurows.dao;

public interface CustmoerDao {
    public void save();
}

spring\src\main\java\com\mercurows\factory\CustomerDaoFactory.java

package com.mercurows.factory;

import com.mercurows.dao.CustmoerDao;
import com.mercurows.dao.impl.CustomerDaoImpl;

public class CustomerDaoFactory {
    public static CustmoerDao getCustmoerDao() {
        return new CustomerDaoImpl();
    }
}

spring\src\main\resources\applicationContext.xml



<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 id="customerDao" class="com.mercurows.factory.CustomerDaoFactory" factory-method="getCustmoerDao"/>
    
beans>

测试

spring\src\main\java\com\mercurows\AppForInstanceCustomer.java

package com.mercurows;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.mercurows.dao.CustmoerDao;

public class AppForInstanceCustomer {
    public static void main(String[] args) {
        // CustmoerDao custmoerDao = CustomerDaoFactory.getCustmoerDao();
        // custmoerDao.save();
        // 3.获取IoC容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        // 4. 获取bean
        CustmoerDao customerDao = (CustmoerDao) ctx.getBean("customerDao");
        customerDao.save();
    }
}

项目结构

spring_第6张图片

方法三:使用实例工厂实例化bean

spring\src\main\java\com\mercurows\dao\BrandDao.java

package com.mercurows.dao;

public interface BrandDao{
    public void save();
}

spring\src\main\java\com\mercurows\dao\impl\BrandDaoImpl.java

package com.mercurows.dao.impl;

import com.mercurows.dao.BrandDao;

public class BrandDaoImpl implements BrandDao{
    public void save() {
        System.out.println("Brand dao save");
    }
}

spring\src\main\java\com\mercurows\factory\BrandDaoFactory.java

package com.mercurows.factory;

import com.mercurows.dao.BrandDao;
import com.mercurows.dao.impl.BrandDaoImpl;

public class BrandDaoFactory {
    public BrandDao getBrandDao() {
        return new BrandDaoImpl();
    }
}

spring\src\main\resources\applicationContext.xml



<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 id = "brandFactory" class="com.mercurows.factory.BrandDaoFactory"/>
    <bean id = "brandDao" factory-method="getBrandDao" factory-bean="brandFactory"/>

beans>

测试

spring\src\main\java\com\mercurows\AppForInstanceBrand.java

package com.mercurows;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.mercurows.dao.BrandDao;

public class AppForInstanceBrand {
    public static void main(String[] args) {
        // 3.获取IoC容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        // 4. 获取bean
        BrandDao brandDao = (BrandDao) ctx.getBean("brandDao");
        brandDao.save();
    }
}

项目结构

spring_第7张图片

方法四:使用FactoryBean实例化bean

该方法为方法三的改良版

在方法三中的 spring\src\main\resources\applicationContext.xml 配置文件写的不简洁,例如 只为了调用工厂中的方法而生成因为非静态方法所以需要先生成工厂类都要指定不同的方法名getBrandDao,没有统一的标准。

具体的,该方法与方法三相比,修改的部分为:

  1. 将工厂spring\src\main\java\com\mercurows\factory\BrandDaoFactoryBean.java 删除修改为

    package com.mercurows.factory;
    
    import org.springframework.beans.factory.FactoryBean;
    import org.springframework.lang.Nullable;
    
    import com.mercurows.dao.BrandDao;
    import com.mercurows.dao.impl.BrandDaoImpl;
    
    // 需要生成什么类就往泛型中放什么类
    public class BrandDaoFactoryBean implements FactoryBean<BrandDao>{
    
        @Override
        @Nullable
        public BrandDao getObject() throws Exception {
            // 替代了原来的BrandDaoFactory中的getBrandDao()方法
            return new BrandDaoImpl();
        }
    
        @Override
        @Nullable
        public Class<?> getObjectType() {
            return BrandDao.class;
        }
    
        // 是否为单例
        @Override
        public boolean isSingleton() {
            return true;
        }
    }
    
  2. 修改了配置文件,spring\src\main\resources\applicationContext.xml

    
    
    <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 id = "brandDao" class="com.mercurows.factory.BrandDaoFactoryBean"/>
    beans>
    
    

其余的部分对于方法三没有改变

项目结构

spring_第8张图片


三、注入

1. setter

上面DI入门中成员对象只有一个,即spring\src\main\java\com\mercurows\service\impl\BookServiceImpl.java 中只有private BookDao bookDao一个成员对象,当需要拥有多个时该如何处理?

多个引用类型成员

下面展示新添private BrandDao brandDao成员对象。

  1. 修改spring\src\main\java\com\mercurows\service\impl\BookServiceImpl.java

    package com.mercurows.service.impl;
    
    import com.mercurows.dao.BookDao;
    import com.mercurows.dao.BrandDao;
    import com.mercurows.service.BookService;
    
    public class BookServiceImpl implements BookService {
        private BookDao bookDao;
    
        private BrandDao brandDao;
    
        public void save() {
            System.out.println("Book service save ");
            bookDao.save();
            brandDao.save();
        }
        public void setBookDao(BookDao bookDao) {
            this.bookDao = bookDao;
        }
        public void setBrandDao(BrandDao brandDao) {
            this.brandDao = brandDao;
        }
    }
    
  2. 修改spring\src\main\resources\applicationContext.xml

    
    
    <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 id="bookDao1" class="com.mercurows.dao.impl.BookDaoImpl"/>
        <bean id="brandDao1" class="com.mercurows.dao.impl.BrandDaoImpl"/>
        
        <bean id="bookService" class="com.mercurows.service.impl.BookServiceImpl">
            
            
            
            <property name= "bookDao" ref="bookDao1"/>
            <property name= "brandDao" ref="brandDao1"/>
        bean>
    beans>
    

多个简单类型成员

现在想要往spring\src\main\java\com\mercurows\dao\impl\BookDaoImpl.java中添加两个新的简单类型成员变量。具体操作如下:

  1. 修改spring\src\main\java\com\mercurows\dao\impl\BookDaoImpl.java文件:

    package com.mercurows.dao.impl;
    
    import com.mercurows.dao.BookDao;
    
    public class BookDaoImpl implements BookDao {
    
        private int connectionNum;
        private String databaseName;
    
        public void save() {
            System.out.println("Book dao save " + connectionNum + "," + databaseName);
        }
    
        public void setConnectionNum(int connectionNum) {
            this.connectionNum = connectionNum;
        }
    
        public void setDatabaseName(String databaseName) {
            this.databaseName = databaseName;
        }
    
    }
    
  2. 修改spring\src\main\resources\applicationContext.xml文件:

    
    
    <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 id="bookDao1" class="com.mercurows.dao.impl.BookDaoImpl">
            
            
            <property name="databaseName" value="mysql"/>
            <property name="connectionNum" value="100"/>
        bean>
        <bean id="brandDao" class="com.mercurows.dao.impl.BrandDaoImpl"/>
        <bean id="bookService" class="com.mercurows.service.impl.BookServiceImpl">
            
            
    
            
            <property name= "bookDao" ref="bookDao1"/>
            <property name= "brandDao" ref="brandDao"/>
        bean>
    beans>
    

2.集合注入

name=“” 参数为类的集合成员对象名字

spring_第9张图片

spring_第10张图片

spring_第11张图片

spring_第12张图片

spring_第13张图片


四、数据源对象管理

修改文件databasesourec\src\main\resources\applicationContext.xml :



<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"
       >
       
       
       
       
       <context:property-placeholder location="classpath*:*.properties" system-properties-mode="NEVER"/>

       
       <bean id="dataSource"  class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
              <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="com.mercurows.dao.impl.BookDaoImpl">
              <property name="databaseName" value="${username}"/>
              <property name="connectionNum" value="100"/>
       bean>
        
beans>

注意上面property中的value值取自:

databasesourec\src\main\resources\jdbc.properties

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/estate
jdbc.username=root

databasesourec\src\main\resources\jdbc2.properties

username=root666
jdbc.password=5508769123

这样上面的值就会注入到databasesourec\src\main\java\com\mercurows\dao\impl\BookDaoImpl.java中对应的成员变量之中去connectionNum,databaseName

package com.mercurows.dao.impl;

import com.mercurows.dao.BookDao;

public class BookDaoImpl implements BookDao {

    private int connectionNum;
    private String databaseName;

    public void save() {
        System.out.println("Book dao save " + connectionNum + "," + databaseName);
    }
    public void setConnectionNum(int connectionNum) {
        this.connectionNum = connectionNum;
    }
    public void setDatabaseName(String databaseName) {
        this.databaseName = databaseName;
    }
}

其他的项目文件:

databasesourec\src\main\java\com\mercurows\dao\BookDao.java

package com.mercurows.dao;

public interface BookDao{
    public void save();
}

databasesourec\src\main\java\com\mercurows\App.java

package com.mercurows;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.mercurows.dao.BookDao;

public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        bookDao.save();
    }
}

pom.xml中的依赖项

<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-contextartifactId>
    <version>6.0.9version>
dependency>

<dependency>
    <groupId>org.mybatisgroupId>
    <artifactId>mybatisartifactId>
    <version>3.5.13version>
dependency>

<dependency>
    <groupId>mysqlgroupId>
    <artifactId>mysql-connector-javaartifactId>
    <version>8.0.30version>
dependency>

<dependency>
    <groupId>com.alibabagroupId>
    <artifactId>druidartifactId>
    <version>1.1.16version>
dependency>

项目结构

spring_第14张图片


五、注解开发

1.注解开发定义bean

spring\src\main\resources\applicationContext.xml组件扫描



<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"
       >
    
    <context:component-scan base-package="com.mercurows"/>
beans>

此表明将会查找com.mercurows目录下的所有使用了@Component("") 注解的类并生成对应bean,例如spring\src\main\java\com\mercurows\dao\impl\BookDaoImpl.java类:

package com.mercurows.dao.impl;

import org.springframework.stereotype.Component;

import com.mercurows.dao.BookDao;

@Component("bookDao")
public class BookDaoImpl implements BookDao {

    private int connectionNum;
    private String databaseName;

    public void save() {
        System.out.println("Book dao save " + connectionNum + "," + databaseName);
    }
    public void setConnectionNum(int connectionNum) {
        this.connectionNum = connectionNum;
    }
    public void setDatabaseName(String databaseName) {
        this.databaseName = databaseName;
    }
}

使用了@Component("bookDao"),则生成了id为bookDao的BookDaoImpl的bean

其实如有需要可以更进一步详细到将改为

测试:

spring\src\main\java\com\mercurows\App2.java

package com.mercurows;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.mercurows.dao.BookDao;
import com.mercurows.service.impl.BookServiceImpl;

public class App2 {
    public static void main(String[] args) {
        // 3.获取IoC容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        // 4. 获取bean
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        System.out.println(bookDao);
    }
}

另外,@Component默认是什么都不写的。所以我们还可以这么写:

spring\src\main\java\com\mercurows\dao\impl\BookDaoImpl.java

package com.mercurows.dao.impl;

import org.springframework.stereotype.Component;

import com.mercurows.dao.BookDao;

@Component
public class BookDaoImpl implements BookDao {

    private int connectionNum;
    private String databaseName;

    public void save() {
        System.out.println("Book dao save " + connectionNum + "," + databaseName);
    }
    public void setConnectionNum(int connectionNum) {
        this.connectionNum = connectionNum;
    }
    public void setDatabaseName(String databaseName) {
        this.databaseName = databaseName;
    }
}

spring\src\main\java\com\mercurows\App2.java

package com.mercurows;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.mercurows.dao.BookDao;
import com.mercurows.service.impl.BookServiceImpl;

public class App2 {
    public static void main(String[] args) {
        // 3.获取IoC容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        // 4. 获取bean
        //因为没有配置id,所以获取bean只能通过class的方式
        BookDao bookDao = (BookDao) ctx.getBean(BookDao.class);
        System.out.println(bookDao);
    }
}

其他文件

spring\src\main\java\com\mercurows\dao\BookDao.java

package com.mercurows.dao;

public interface BookDao{
    public void save();
}

项目结构

spring_第15张图片

2.纯注解开发

使用纯注解开发可以达到不用写配置文件的目的

  1. 创建配置spring类

    spring_annotation_bean\src\main\java\com\mercurows\config\SpringConfig.java

    package com.mercurows.config;
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    
    //@Configuration表明这是配置类
    @Configuration
    //@ComponentScan代替xml配置文件中的包扫描
    // @ComponentScan(com.mercurows)
    //或是更为细节的:
    @ComponentScan({ "com.mercurows.dao", "com.mercurows.service" })
    public class SpringConfig {
    }
    
  2. 其他类

    spring_annotation_bean\src\main\java\com\mercurows\dao\impl\BookDaoImpl.java

    package com.mercurows.dao.impl;
    
    import org.springframework.context.annotation.Scope;
    import org.springframework.stereotype.Component;
    import org.springframework.stereotype.Repository;
    import com.mercurows.dao.BookDao;
    
    import javax.annotation.PostConstruct;
    import javax.annotation.PreDestroy;
    
    //bean的id
    @Component("bookDao")
    @Repository
    //表示生成的bean为单例模式
    @Scope("singleton")
    public class BookDaoImpl implements BookDao {
    
        private int connectionNum;
        private String databaseName;
    
        public void save() {
            System.out.println("Book dao save " + connectionNum + "," + databaseName);
        }
        public void setConnectionNum(int connectionNum) {
            this.connectionNum = connectionNum;
        }
        public void setDatabaseName(String databaseName) {
            this.databaseName = databaseName;
        }
    }
    

    spring_annotation_bean\src\main\java\com\mercurows\dao\BookDao.java

    package com.mercurows.dao;
    
    public interface BookDao{
        public void save();
    }
    

    spring_annotation_bean\src\main\java\com\mercurows\service\impl\BookServiceImpl.java

    package com.mercurows.service.impl;
    
    import org.springframework.stereotype.Component;
    
    import com.mercurows.service.BookService;
    
    @Component
    public class BookServiceImpl implements BookService {
    
        public void save() {
            System.out.println("Book service save ");
        }
    }
    

    spring_annotation_bean\src\main\java\com\mercurows\service\BookService.java

    package com.mercurows.service;
    
    public interface BookService {
        public void save();
    }
    
  3. 测试类

    spring_annotation_bean\src\main\java\com\mercurows\App.java

    package com.mercurows;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    import com.mercurows.config.SpringConfig;
    import com.mercurows.dao.BookDao;
    import com.mercurows.service.BookService;
    
    public class App
    {
        public static void main( String[] args )
        {
            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);
        }
    }
    
  4. 项目结构

    spring_第16张图片

3.注解开发依赖注入

注解开发依赖注入是在纯注解开发基础上修改而来的,即下面的项目是在上面项目基础上修改的。由于纯注解开发没有了配置文件,因而对于一些引用对象就无法正确注解。

spring_annotation_bean\src\main\java\com\mercurows\service\impl\BookServiceImpl.java

package com.mercurows.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import com.mercurows.dao.BookDao;
import com.mercurows.service.BookService;

@Service
public class BookServiceImpl implements BookService {
    private BookDao bookDao;
	public BookServiceImpl(BookDao bookDao){
        this.bookDao = bookDao;
    }
    public void save() {
        System.out.println("Book service save ");
        bookDao.save();
    }
}

虽然上面有带参的构造函数BookServiceImpl,但是并不会执行,因此成员对象bookDao为null ,需要进行如如下修改:

package com.mercurows.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import com.mercurows.dao.BookDao;
// import com.mercurows.dao.BrandDao;
import com.mercurows.service.BookService;

@Service
public class BookServiceImpl implements BookService {
    // 自动装配,按照BookDao类名来,spring框架中使用具体的实现类作为Bean
    @Autowired
    private BookDao bookDao;

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

填写了自动装载@Autowired就不用专门为成员引用变量编写setter函数了。

注意点:

在Spring框架中,通常将接口定义和接口的实现类分开管理。在依赖注入(DI)中,我们通过接口编程实现松耦合的设计,以便在运行时动态地决定使用哪个具体的实现类。

但在实际应用中,当我们使用注解来标记Bean时,Spring会自动扫描并创建Bean的实例。默认情况下,Spring将使用具体的实现类作为Bean,而不是接口本身。这样做的原因是因为Spring无法直接实例化接口,而只能实例化具体的

例如上面项目中,注解@Service打在BookDaoImpl类上而非BookDao类上,但是方法却是:getBean(BookDao.class)

如这个项目中,BookDaoImpl类实现了BookDao接口,因此,BookServiceImpl自动装填成员变量BookDao bookDao时将会使用BookDaoImpl来作为bean,这时如果还有一个BookDaoImpl2类实现了BookDao接口的话那么BookDaoImpl2也可以作为bean来实例化类BookServiceImpl,这时就会出现混乱。报错:

Exception in thread “main” org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘bookServiceImpl’: Unsatisfied dependency expressed through field ‘bookDao’: No qualifying bean of type ‘com.mercurows.dao.BookDao’ available: expected single matching bean but found 2: bookDaoImpl,bookDaoImpl2

解决方法为,修改如下文件:

spring_annotation_bean\src\main\java\com\mercurows\dao\impl\BookDaoImpl.java

添加bean的id

package com.mercurows.dao.impl;

import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import com.mercurows.dao.BookDao;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

//bean的id
@Component("bookDao")
@Repository
public class BookDaoImpl implements BookDao {

    private int connectionNum;
    private String databaseName;

    public void save() {
        System.out.println("Book dao save " + connectionNum + "," + databaseName);
    }
    @PostConstruct
    public void init() {
        System.out.println("init ...");
    }
    @PreDestroy
    public void destroy() {
        System.out.println("destroy ...");
    }
    public void setConnectionNum(int connectionNum) {
        this.connectionNum = connectionNum;
    }
    public void setDatabaseName(String databaseName) {
        this.databaseName = databaseName;
    }
}

spring_annotation_bean\src\main\java\com\mercurows\dao\impl\BookDaoImpl2.java

添加bean的id

package com.mercurows.dao.impl;

import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import com.mercurows.dao.BookDao;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

//bean的id
@Component("bookDao2")
@Repository
public class BookDaoImpl2 implements BookDao {

    private int connectionNum;
    private String databaseName;

    public void save() {
        System.out.println("Book dao save 2 " + connectionNum + "," + databaseName);
    }
    @PostConstruct
    public void init() {
        System.out.println("init ...");
    }
    @PreDestroy
    public void destroy() {
        System.out.println("destroy ...");
    }
    public void setConnectionNum(int connectionNum) {
        this.connectionNum = connectionNum;
    }
    public void setDatabaseName(String databaseName) {
        this.databaseName = databaseName;
    }
}

spring_annotation_bean\src\main\java\com\mercurows\service\impl\BookServiceImpl.java

装填时指定bean的id

package com.mercurows.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import com.mercurows.dao.BookDao;
// import com.mercurows.dao.BrandDao;
import com.mercurows.service.BookService;

@Service
public class BookServiceImpl implements BookService {
    // 自动装配
    @Autowired
    @Qualifier("bookDao")
    private BookDao bookDao;

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

测试类

spring_annotation_bean\src\main\java\com\mercurows\App.java

package com.mercurows;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.mercurows.config.SpringConfig;
import com.mercurows.service.BookService;

public class App
{
    public static void main( String[] args )
    {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookService bookService = ctx.getBean(BookService.class);
        bookService.save();
    }
}

4.注解开发管理第三方bean

管理Druid数据源

配置文件

spring_annotation_third_bean_manager\src\main\java\com\mercurows\config\SpringConfig.java

package com.mercurows.config;

import javax.sql.DataSource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.alibaba.druid.pool.DruidDataSource;

@Configuration
public class SpringConfig {
    // 1.定义一个方法获得要管理的对象
    // 2。添加@Bean,表示当前方法的返回值是一个bean
    @Bean
    public DataSource dataSource() {
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/estate");
        ds.setUsername("root");
        ds.setPassword("5508769123");
        return ds;
    }
}
/*
 * The purpose of @Bean is to explicitly declare beans and their dependencies,
 * and it is an alternative to using component scanning for bean discovery.
 * When using @Bean, Spring will directly invoke the method to create the bean instance,
 * and there's no need for component scanning to search for classes marked with specific annotations.
 */

测试

spring_annotation_third_bean_manager\src\main\java\com\mercurows\App.java

package com.mercurows;

import javax.sql.DataSource;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.mercurows.config.SpringConfig;

public class App
{
    public static void main( String[] args )
    {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        // 根据类型名返回对应的Bean
        DataSource dataSource = ctx.getBean(DataSource.class);
        System.out.println(dataSource);
    }
}

但是若是以后有什么第三方bean都往配置文件SpringConfig.java中写的话,这个文件就会显得臃肿,因此可以采取将其分离方法,使用@Import导入对应的分离部分。

spring_annotation_third_bean_manager\src\main\java\com\mercurows\config\SpringConfig.java

package com.mercurows.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
// @Import注解只允许写一次,多个数据用数组形式
@Import({JdbcConfig.class})
public class SpringConfig {
}
/*
 * If you define a bean using @Bean without specifying any scope,
 * it will be a singleton by default,
 * and there will be only one instance of that bean throughout the application context.
 * The bean will be visible and accessible within the application context,
 * but its visibility does not extend beyond it.
 * If you need the bean to be visible and accessible across different application contexts
 * (e.g., in a multi-module project),
 * you can consider using Spring's support for importing configurations from other modules or using Spring's context hierarchy.
 */

spring_annotation_third_bean_manager\src\main\java\com\mercurows\config\JdbcConfig.java

package com.mercurows.config;

import javax.sql.DataSource;

import org.springframework.context.annotation.Bean;

import com.alibaba.druid.pool.DruidDataSource;

public class JdbcConfig {
    // 1.定义一个方法获得要管理的对象
    // 2。添加@Bean,表示当前方法的返回值是一个bean
    @Bean
    public DataSource dataSource() {
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/estate");
        ds.setUsername("root");
        ds.setPassword("5508769123");
        return ds;
    }
}
/*
 * The purpose of @Bean is to explicitly declare beans and their dependencies,
 * and it is an alternative to using component scanning for bean discovery.
 * When using @Bean, Spring will directly invoke the method to create the bean instance,
 * and there's no need for component scanning to search for classes marked with specific annotations.
 */

注解开发为第三方bean注入

简单类型的注入

使用@Value注解简单类型

spring_annotation_third_bean_manager\src\main\java\com\mercurows\config\JdbcConfig.java

package com.mercurows.config;

import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;

import com.alibaba.druid.pool.DruidDataSource;
@PropertySource("jdbc.properties") // 指定属性文件的位置
public class JdbcConfig {
    // private static final Logger logger = LoggerFactory.getLogger(JdbcConfig.class);
    @Value("${jdbc.driver}")
    private String driver;

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

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

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

    // 1.定义一个方法获得要管理的对象
    // 2。添加@Bean,表示当前方法的返回值是一个bean
    @Bean
    public DataSource dataSource() {
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        // logger.info("Driver: {}", driver);
        return ds;
    }
}

这里的@Value也可以直接写死你需要的数据在里面,这里采用读取配置文件的方式。

spring_annotation_third_bean_manager\src\main\resources\jdbc.properties

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/estate
jdbc.username=root
jdbc.password=5508769123

spring_annotation_third_bean_manager\src\main\java\com\mercurows\config\SpringConfig.java

package com.mercurows.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
// @Import注解只允许写一次,多个数据用数组形式
@Import({JdbcConfig.class})
public class SpringConfig {
}

测试

spring_annotation_third_bean_manager\src\main\java\com\mercurows\App.java

package com.mercurows;

import javax.sql.DataSource;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.mercurows.config.SpringConfig;

public class App
{
    public static void main( String[] args )
    {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        // 根据类型名返回对应的Bean
        DataSource dataSource = ctx.getBean(DataSource.class);
        System.out.println(dataSource);
    }
}

项目结构

spring_第17张图片

引用类型的注入

在类spring_annotation_third_bean_manager\src\main\java\com\mercurows\config\JdbcConfig.java中注方法注入BookDao引用对象

package com.mercurows.config;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;

import com.alibaba.druid.pool.DruidDataSource;
import com.mercurows.dao.BookDao;
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;

    // 1.定义一个方法获得要管理的对象
    // 2。添加@Bean,表示当前方法的返回值是一个bean
    @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;
    }
}

添加了@Bean注解,因此该方法的参数会使用自动装配,即自动寻找标记了实现了BookDao接口的且标记了bean的类。

spring_annotation_third_bean_manager\src\main\java\com\mercurows\dao\impl\BookDaoImpl.java 添加@Repository注解生成bean

package com.mercurows.dao.impl;

import org.springframework.stereotype.Repository;

import com.mercurows.dao.BookDao;

@Repository
public class BookDaoImpl implements BookDao {

    private int connectionNum;
    private String databaseName;

    public void save() {
        System.out.println("Book dao save " + connectionNum + "," + databaseName);
    }
    public void setConnectionNum(int connectionNum) {
        this.connectionNum = connectionNum;
    }
    public void setDatabaseName(String databaseName) {
        this.databaseName = databaseName;
    }
}

spring_annotation_third_bean_manager\src\main\java\com\mercurows\dao\BookDao.java

package com.mercurows.dao;

public interface BookDao{
    public void save();
}

spring_annotation_third_bean_manager\src\main\java\com\mercurows\config\SpringConfig.java采用包扫描,扫描自动装配所需的bean的类路径

package com.mercurows.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@PropertySource("classpath:jdbc.properties") // 指定属性文件的位置
//包扫描
@ComponentScan("com.mercurows")
// @Import注解只允许写一次,多个数据用数组形式
@Import({JdbcConfig.class})
public class SpringConfig {
}

测试类

spring_annotation_third_bean_manager\src\main\java\com\mercurows\App.java

package com.mercurows;

import javax.sql.DataSource;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.mercurows.config.SpringConfig;

public class App
{
    public static void main( String[] args )
    {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        // 根据类型名返回对应的Bean
        DataSource dataSource = ctx.getBean(DataSource.class);
        System.out.println(dataSource);
    }
}

项目结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wEwoi5Q9-1691158236097)(C:/Users/Mercurows/AppData/Roaming/Typora/typora-user-images/image-20230726183856647.png)]


六、整合mybatis

pom.xml依赖

spriong_mybatis\pom.xml

    
    <dependency>
      <groupId>org.springframeworkgroupId>
      <artifactId>spring-jdbcartifactId>
      <version>5.3.23version>
    dependency>

    
    <dependency>
      <groupId>org.springframeworkgroupId>
      <artifactId>spring-contextartifactId>
      <version>5.3.23version>
    dependency>

    
    <dependency>
      <groupId>org.slf4jgroupId>
      <artifactId>slf4j-apiartifactId>
      <version>1.7.20version>
    dependency>

    
    <dependency>
      <groupId>ch.qos.logbackgroupId>
      <artifactId>logback-classicartifactId>
      <version>1.2.3version>
    dependency>

    
    <dependency>
      <groupId>ch.qos.logbackgroupId>
      <artifactId>logback-coreartifactId>
      <version>1.4.8version>
    dependency>

    <dependency>
      <groupId>javax.annotationgroupId>
      <artifactId>javax.annotation-apiartifactId>
      <version>1.3.2version>
  dependency>

  <dependency>
    <groupId>mysqlgroupId>
    <artifactId>mysql-connector-javaartifactId>
    <version>8.0.30version>
  dependency>

  <dependency>
    <groupId>com.alibabagroupId>
    <artifactId>druidartifactId>
    <version>1.1.16version>
  dependency>

  
  <dependency>
    <groupId>org.mybatisgroupId>
    <artifactId>mybatisartifactId>
    <version>3.5.6version>
  dependency>

    <dependency>
      <groupId>org.mybatisgroupId>
      <artifactId>mybatis-springartifactId>
      <version>2.0.6version>
    dependency>

    
    <dependency>
      <groupId>org.springframework.batchgroupId>
      <artifactId>spring-batch-infrastructureartifactId>
      <version>4.3.0version>
    dependency>

配置类

获取数据源

spriong_mybatis\src\main\java\com\mercurows\config\JdbcConfig.java

package com.mercurows.config;

import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;

import com.alibaba.druid.pool.DruidDataSource;

public class JdbcConfig {
    //private static final Logger logger = LoggerFactory.getLogger(JdbcConfig.class);
    @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
    DataSource dataSource() {
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(username);
        ds.setPassword(password);
        //logger.info("Driver: {}", driver);
        return ds;
    }
}

MyBatis配置

spriong_mybatis\src\main\java\com\mercurows\config\MybatisConfig.java

代替mybatis-config.xml配置文件

package com.mercurows.config;

import javax.sql.DataSource;

import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;

// 代替mybatis-config.xml配置文件生成SqlSession的功能
public class MybatisConfig {
    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) {
        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
        // 代替mybatis配置文件:mybatis-config.xml 中
        // 的语句
        ssfb.setTypeAliasesPackage("com.mercurows.domain");
        ssfb.setDataSource(dataSource);
        return ssfb;
    }

    //加载sql映射文件 这个项目中是 CityDao.java 文件
    // 代替mybatis配置文件:mybatis-config.xml 中包扫描
    // 的语句
    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer() {
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        msc.setBasePackage("com.mercurows.dao");
        return msc;
    }
}

/* 一些注解
通过 setTypeAliasesPackage() 方法设置了类型别名的包路径。
在这个例子中,指定的包路径是 "com.mercurows.domain",
这意味着所有在 com.mercurows.domain 包下的Java类都将被当作类型别名。
假设在这个包下有一个名为 User 的Java类,
设置了类型别名后,你可以在MyBatis的Mapper文件中直接使用 User 来代表这个类,
而不需要使用完整的包名路径,比如不再需要写成 com.mercurows.domain.User。
这样做的好处是在Mapper文件中可以更加简洁地引用Java类,
尤其是在涉及多个Java类的情况下,可以减少冗余的代码。
同时,如果你在Mapper文件中引用了某个Java类作为结果集的返回类型或参数类型,
MyBatis会自动将其与数据库中的记录进行映射。
 */

/*

是在XML配置文件中使用的代码,用于通过XML配置方式设置MyBatis的类型别名。
它也是告诉MyBatis去扫描指定包下的Java类,并将这些类设置为类型别名。
 */

spring配置文件

spriong_mybatis\src\main\java\com\mercurows\config\SpringConfig.java

package com.mercurows.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;

@Configuration
//包扫描,对应bean的创建识别
@ComponentScan("com.mercurows")
// 导入JdbcConfig.java文件需要读取的数据库信息文件(jdbc.properties)位置
// classpath,表示该项目下的所有class文件均为读取目标
@PropertySource("classpath:jdbc.properties")
@Import({ JdbcConfig.class, MybatisConfig.class })
public class SpringConfig {
}

数据库文件

spriong_mybatis\src\main\resources\jdbc.propertiesn

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/estate
jdbc.username=root
jdbc.password=5508769123

其他类

spriong_mybatis\src\main\java\com\mercurows\dao\CityDao.java

package com.mercurows.dao;

import java.util.List;

import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import com.mercurows.domain.City;

// 使用MyBatis的注解方式来定义SQL语句
public interface CityDao {
    @Insert("insert into city(name, prov_id) calue(#{name},#{prov_id})")
    void save(City city);

    @Delete("delete from city where id = #{id}")
    void delete(Integer id);

    @Update("update city set name = #{name},prov_id = #{prov_id} where id = #{id}")
    void update(City city);

    @Select("select * from city")
    List<City> findAll();

    @Select("select * from city where id = #{id}")
    City findById(Integer id);
}

spriong_mybatis\src\main\java\com\mercurows\domain\City.java

package com.mercurows.domain;

public class City {
    private Integer id;
    private String name;
    private Integer prov_id;
    public void setId(Integer id) {
        this.id = id;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setProv_id(Integer prov_id) {
        this.prov_id = prov_id;
    }
    public Integer getId() {
        return id;
    }
    public String getName() {
        return name;
    }
    public Integer getProv_id() {
        return prov_id;
    }
    @Override
    public String toString() {
        return "Account [id=" + id + ", name=" + name + ", prov_id=" + prov_id + "]";
    }

}

spriong_mybatis\src\main\java\com\mercurows\service\CitySrvice.java

package com.mercurows.service;

import java.util.List;

import com.mercurows.domain.City;

public interface CitySrvice {
    public void save(City city);

    public void update(City city);

    public void delete(Integer id);

    public City findById(Integer id);

    public List<City> findAll();
}

spriong_mybatis\src\main\java\com\mercurows\service\impl\CityServiceImpl.java

package com.mercurows.service.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.mercurows.dao.CityDao;
import com.mercurows.domain.City;
import com.mercurows.service.CitySrvice;

@Service
public class CityServiceImpl implements CitySrvice{

    @Autowired
    private CityDao cityDao;

    @Override
    public void save(City city) {
        cityDao.save(city);
    }

    @Override
    public void update(City city) {
        cityDao.update(city);
    }

    @Override
    public void delete(Integer id) {
        cityDao.delete(id);
    }

    @Override
    public City findById(Integer id) {
        return cityDao.findById(id);
    }

    @Override
    public List<City> findAll() {
        return cityDao.findAll();
    }

}

spriong_mybatis\src\main\java\com\mercurows\App.java

package com.mercurows;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.mercurows.config.SpringConfig;
import com.mercurows.domain.City;
import com.mercurows.service.impl.CityServiceImpl;

public class App
{
    public static void main( String[] args )
    {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        CityServiceImpl cityService = ctx.getBean(CityServiceImpl.class);

        City city = cityService.findById(80000);
        System.out.println(city);
    }
}

项目结构

spring_第18张图片


七、AOP

1.AOP切入点表达式

pom.xml依赖

    <dependency>
      <groupId>org.springframeworkgroupId>
      <artifactId>spring-contextartifactId>
      <version>5.3.23version>
    dependency>

    <dependency>
      <groupId>org.aspectjgroupId>
      <artifactId>aspectjweaverartifactId>
      <version>1.9.4version>
    dependency>

spring_aop\src\main\java\com\mercurows\dao\BookDao.java

package com.mercurows.dao;

public interface BookDao {
    public void save();
    public void update();
}

spring_aop\src\main\java\com\mercurows\dao\impl\BookDaoImpl.java

package com.mercurows.dao.impl;

import org.springframework.stereotype.Repository;

import com.mercurows.dao.BookDao;

@Repository
public class BookDaoImpl implements BookDao {
    @Override
    public void save() {
        System.out.println(System.currentTimeMillis());
        System.out.println("book dao save");
    }
    @Override
    public void update(){
        System.out.println("book dao update");
    }

}

如何在在不修改上面代码的情况下可以让 public void update()方法也能执行System.out.println(System.currentTimeMillis());语句呢?使用AOP可以解决。

AOP切面配置

spring_aop\src\main\java\com\mercurows\apo\MyAdvice.java

package com.mercurows.apo;

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

// 使该类成为bean而受spring控制
@Component
// 告诉spring 扫描到该类时将其当成aop处理
@Aspect
public class MyAdvice {
    // 设置切入点-方法:
    // 1.使用接口形式
    // 2.使用实现类形式:@Pointcut("execution(void com.mercurows.dao.BookDaoImpl.update())")
    @Pointcut("execution(void com.mercurows.dao.BookDao.update())")
    // 设置空壳的私有方法作为切入点
    private void pt(){}

    // 声明在上面的切入点执行之前执行该函数
    @Before("pt()")
    public void method() {
        System.out.println(System.currentTimeMillis());
    }
}

测试

spring_aop\src\main\java\com\mercurows\App.java

package com.mercurows;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.mercurows.config.SpringConfig;
import com.mercurows.dao.BookDao;

public class App
{
    public static void main( String[] args )
    {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookDao bookDao = ctx.getBean(BookDao.class);
        // bookDao.save();
        bookDao.update();
    }
}

输出:

1690649016097
book dao update

可以看出,可以让 public void update()方法也能执行System.out.println(System.currentTimeMillis());语句

spring_aop\src\main\java\com\mercurows\config\SpringConfig.java

package com.mercurows.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan("com.mercurows")
// 告诉spring该项目中有使用aop开发的东西
// 与 MyAdvice.java 中的@Aspect配套
@EnableAspectJAutoProxy

public class SpringConfig {
}

项目结构

spring_第19张图片

2.AOP通知类型

spring_第20张图片

spring_第21张图片

spring_第22张图片

spring_第23张图片

spring_aop\src\main\java\com\mercurows\dao\impl\BookDaoImpl.java

package com.mercurows.dao.impl;

import org.springframework.stereotype.Repository;

import com.mercurows.dao.BookDao;

@Repository
public class BookDaoImpl implements BookDao {
    @Override
    public void save() {
        System.out.println(System.currentTimeMillis());
        System.out.println("book dao save is running");
    }
    @Override
    public void update(){
        System.out.println("book dao update is running");
    }
    @Override
    public int select() {
        System.out.println("book dao select is running");
        return 100;
    }

}

spring_aop\src\main\java\com\mercurows\dao\BookDao.java

package com.mercurows.dao;

public interface BookDao {
    public void save();

    public void update();

    public int select();
}

spring_aop\src\main\java\com\mercurows\apo\MyAdvice.java

package com.mercurows.apo;

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

// 使该类成为bean而受spring控制
@Component
// 告诉spring 扫描到该类时将其当成aop处理
@Aspect
public class MyAdvice {
    // 设置切入点-方法:
    // 1.使用接口形式
    // 2.使用实现类形式:@Pointcut("execution(void com.mercurows.dao.BookDaoImpl.update())")
    @Pointcut("execution(void com.mercurows.dao.BookDao.update())")
    // 设置空壳的私有方法作为切入点
    private void pt() {
    }

    @Pointcut("execution(int com.mercurows.dao.BookDao.select())")
    private void pt2(){}

    // 声明在上面的切入点执行之前执行该函数
    @Before("pt()")
    public void before() {
        System.out.println("before advice..");
    }

    // 声明在在上面的切入点执行之前执行该函数
    @After("pt()")
    public void after() {
        System.out.println("after advice..");
    }

    // 声明在在上面的切入点执行前后执行该函数
    @Around("pt2()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("around before advice..");
        // 表示对原始操作的调用
        // 环绕通知,可以接受原方法的返回值,最后再将其返回
        Integer ret = (Integer)pjp.proceed();
        System.out.println("around after advice");
        return ret;
    }

}

3.AOP通知获取数据

4.获取参数数据&获取返回值&获取异常

spring_aop_advice_data\src\main\java\com\mercurows\dao\impl\BookDaoImlpl.java

package com.mercurows.dao.impl;

import org.springframework.stereotype.Repository;

import com.mercurows.dao.BookDao;

@Repository
public class BookDaoImlpl implements BookDao{

    @Override
    public String findName(int id,String name) {
        System.out.println("id:" + id + "name:" + name);
        // if (true)
        //     throw new NullPointerException();
        return "itcast";
    }
}

spring_aop_advice_data\src\main\java\com\mercurows\apo\MyAdvic.java

package com.mercurows.apo;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

// 使该类成为bean而受spring控制
@Component
// 告诉spring 扫描到该类时将其当成aop处理
@Aspect
public class MyAdvic {
    // 切入表达式使用了通配符"*",表示匹配任何返回类
    // ".."表示该方法可以接受任意数量的参数
    @Pointcut("execution(* com.mercurows.dao.BookDao.findName(..))")
    private void pt() {
    }

    @Before("pt()")
    public void before(JoinPoint jp) {
        // 获取原方法的参数
        Object[] ars = jp.getArgs();
        System.out.println(Arrays.toString(ars));
        System.out.println("before advice");
    }
    @After("pt()")
    public void after(JoinPoint jp) {
        // 获取原方法的参数
        Object[] ars = jp.getArgs();
        System.out.println(Arrays.toString(ars));
        System.out.println("aftr advice");
    }
    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp){
        // 获取原方法的参数
        Object[] args = pjp.getArgs();
        System.out.println(Arrays.toString(args));
        // 修改原方法的参数
        args[0] = 666;
        // 返回被修改过后的参数到原方法中
        Object ret = null;
        try {
            ret = pjp.proceed(args);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return ret;
    }
    // 告诉知,如果该方法有返回值
    // 则其为ret,对应afterReturning中的形参
    // 注意:如果原方法是带参数的,则注解书写顺序有要求必须JoinPoint在前如:
    // public void afterReturning(JoinPoint jp,Object ret)
    @AfterReturning(value = "pt()" , returning = "ret")
    public void afterReturning(Object ret) {
        System.out.println("afterReturning advice  " + ret);
    }
    // 给个形参来接受异常对象
    @AfterThrowing(value = "pt()" , throwing = "t")
    public void afterThrowing(Throwable t) {
        System.out.println("afterThrowing advice  " + t);
    }
}

八、事务管理

简单入门

配置

spring_affairs\src\main\java\com\mercurows\config\JdbcConfig.java

package com.mercurows.config;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import com.alibaba.druid.pool.DruidDataSource;

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
    DataSource dataSource() {
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(username);
        ds.setPassword(password);
        return ds;
    }

    // 2. 设置事务管理器
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
        return transactionManager;
    }
}

spring_affairs\src\main\java\com\mercurows\config\MybatisConfig.java

package com.mercurows.config;

import javax.sql.DataSource;

import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;

public class MybatisConfig {
    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
        ssfb.setTypeAliasesPackage("com.mercurows.domain");
        ssfb.setDataSource(dataSource);
        return ssfb;
    }

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer() {
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        msc.setBasePackage("com.mercurows.dao");
        return msc;
    }
}

spring_affairs\src\main\java\com\mercurows\config\SpringConfig.java

package com.mercurows.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@ComponentScan({"com.mercurows.service" })
@PropertySource("classpath:jdbc.properties")
@Import({ JdbcConfig.class, MybatisConfig.class })
// 3.开启注解式事务驱动
@EnableTransactionManagement
public class SpringConfig {
}

spring_affairs\src\main\java\com\mercurows\dao\AccountDao.java

package com.mercurows.dao;

import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;

public interface AccountDao {
    @Update("update account set money = money + #{money} where name = #{name}")
    void inMoney(@Param("name") String name, @Param("money") Double money);

    @Update("update account set money = money - #{money} where name= #{name}")
    void outMoney(@Param("name") String name, @Param("money") Double mmoney);
}

spring_affairs\src\main\java\com\mercurows\domain\Acount.java

package com.mercurows.domain;

public class Acount {
    private int id;
    private String name;
    private Double money;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Double getMoney() {
        return money;
    }
    public void setMoney(Double money) {
        this.money = money;
    }
    @Override
    public String toString() {
        return "Acount [id=" + id + ", name=" + name + ", money=" + money + "]";
    }
}

spring_affairs\src\main\java\com\mercurows\service\impl\AccountServiceImpl.java

package com.mercurows.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.mercurows.dao.AccountDao;
import com.mercurows.service.AccountService;

@Service
public class AccountServiceImpl implements AccountService{

    @Autowired
    private AccountDao accountDao;

    @Override
    public void transfer(String out, String in, double money) {
        // System.out.println("-----------------AccountDao is : "+accountDao);
        accountDao.outMoney(out, money);
        //人为制造异常
        int i = 1/0;
        accountDao.inMoney(in, money);
    }
}

spring_affairs\src\main\java\com\mercurows\service\AccountService.java

package com.mercurows.service;

import org.springframework.transaction.annotation.Transactional;

public interface AccountService {
    /*
     * 转账操作
     * @param out 传出方
     * @param in 转入方
     * @param money 金额
     */
    // 1.在业务层接口添加spring事务管理
    @Transactional
    public void transfer(String out, String in, double money);
}

test类

spring_affairs\src\test\java\com\mercurows\AccountServiceTest.java

package com.mercurows;

import static org.junit.Assert.assertTrue;

import java.io.IOException;

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

import com.mercurows.config.SpringConfig;
import com.mercurows.service.AccountService;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest
{
    @Autowired
    private AccountService accountService;

    @Test
    public void testTransfer() throws IOException {
        accountService.transfer("小明", "小黄", 100D);
    }
}

开启了事务管理之后,AccountServiceImpl的transfer方法中的accountDao.outMoney()

accountDao.inMoney()方法就同成功同失败了,即要么同时更新数据库要么同时有一个方法异常就同时回滚事务。

说明

事务管理只比前面的普通项目多了代码注解里的三步骤:

// 1.在业务层接口添加spring事务管理

// 2. 设置事务管理器

// 3.开启注解式事务驱动

碎碎念: 这里有个接口AccountDao其并没有添加注解@Component也没有具体的添加了@Component的实现类,但是在AccountServiceImpl类中却对成员AccountDao使用了@Autowired。

这是因为:MybatisConfig.java类中使用了包扫描,将com.mercurows.dao下的所有接口都注册成了 Mapper接口。这些接口都会被 MyBatis 用于创建 Mapper 接口的代理对象,那么 Spring 将会创建相应的 Mapper 代理对象,可以通过 @Autowired 进行自动装配。

    MapperScannerConfigurer msc = new MapperScannerConfigurer();
    msc.setBasePackage("com.mercurows.dao");

项目结构

spring_第24张图片

事务传播行为

现有这么个任务:spring_第25张图片

新添类与接口:

spring_affairs\src\main\java\com\mercurows\dao\LogDao.java

package com.mercurows.dao;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;

public interface LogDao {
    @Insert("insert into log(info,createDate) values(#{info},now()) ")
    void log(@Param("info") String info);
}

spring_affairs\src\main\java\com\mercurows\service\LogService.java

package com.mercurows.service;

import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

public interface LogService {
    // 开启一个船新的事务
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    void log(String out, String in, Double money);
}

spring_affairs\src\main\java\com\mercurows\service\impl\LogServiceImpl.java

package com.mercurows.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.mercurows.dao.LogDao;
import com.mercurows.service.LogService;

@Service
public class LogServiceImpl implements LogService{

    @Autowired
    private LogDao logDao;
    @Override
    public void log(String out, String in, Double money) {
        logDao.log("转账操作由" + out + "到" + in + "金额 :" + money);
    }
}

spring_affairs\src\main\java\com\mercurows\service\impl\AccountServiceImpl.java

package com.mercurows.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.mercurows.dao.AccountDao;
import com.mercurows.service.AccountService;
import com.mercurows.service.LogService;

@Service
public class AccountServiceImpl implements AccountService{

    @Autowired
    private AccountDao accountDao;

    @Autowired
    private LogService logService;

    @Override
    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);
        }
    }
}

需要特别注意上面spring_affairs\src\main\java\com\mercurows\service\LogService.java中的:@Transactional(propagation = Propagation.REQUIRES_NEW)

指定了propagation = Propagation.REQUIRES_NEW 即开启了新的事务

spring_第26张图片

注解的一种配置方式。它指定了方法的事务传播行为为 REQUIRES_NEW,意味着每次调用该方法都会创建一个新的独立事务,无论当前是否已经存在其他事务。

  1. 当调用一个标记为 @Transactional(propagation = Propagation.REQUIRES_NEW) 的方法时,Spring 将会创建一个新的事务,并让该方法在这个新事务中运行。
  2. 如果当前存在其他事务(外部事务),调用这个方法将导致外部事务挂起,该方法在自己的独立事务中运行。
  3. 该方法执行完毕后,如果没有发生异常,它会提交自己的事务。提交的含义是将事务的修改持久化到数据库。
  4. 如果该方法执行过程中发生异常,则会回滚该方法的事务,即撤销事务执行过程中对数据库的修改。
  5. 外部事务不会受到内部事务(即被 REQUIRES_NEW 标记的方法)的提交或回滚影响,内外事务是完全独立的。

如果只写@Transactional的话默认为REQUIRED:

如果当前存在事务,则加入到当前事务中;如果当前没有事务,则创建一个新事务。

spring_第27张图片

即transfer方法里面的三个事务是一起的。因此三个方法之能同时成功同时失败,不符合预期。

不过我一开的想法是将log()方法做成普通方法而不是事务,这样也能实现该目的。。

进行修改:

spring_affairs\src\main\java\com\mercurows\service\LogService.java该接口不使用事务,当成普通的方法。

package com.mercurows.service;
public interface LogService {
    void log(String out, String in, Double money);
}

spring_affairs\src\main\java\com\mercurows\service\impl\AccountServiceImpl.java

package com.mercurows.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.mercurows.dao.AccountDao;
import com.mercurows.service.AccountService;
import com.mercurows.service.LogService;

@Service
public class AccountServiceImpl implements AccountService{

    @Autowired
    private AccountDao accountDao;

    @Autowired
    private LogService logService;

    @Override
    public void transfer(String out, String in, double money) {
        try {
            accountDao.outMoney(out, money);
            int i = 1 / 0;
            accountDao.inMoney(in, money);
        } catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            logService.log(out, in, money);
        }
    }
}

在一个事务方法中,当某一段代码(无论是否是在 finally 块中)出现异常时,整个事务都会被回滚,包括事务方法内部调用的非事务方法也会回滚。

由于异常被捕获并在 catch 块中处理,程序不会继续向上层抛出异常,因此事务不会回滚。

你可能感兴趣的:(spring,java,后端)