4.9 @Bean
作用:将方法的返回值对象放入Spring容器中。如果想将第三方类的对象放入容器,可以使用@Bean
位置:配置类的方法上方。
属性:name:给bean对象设置id
注意:@Bean修饰的方法如果有参数,spring会根据参数类型从容器中查找可用对象。
举例:如果想将jdbc连接对象放入Spring容器,我们无法修改Connection源码添加@Component,此时就需要使用将@Bean该对象放入Spring容器
1、添加驱动依赖
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.27</version> </dependency>
2、将Connection对象放入Spring容器
@Bean(name = "connection") public Connection getConnection(){ try { Class.forName("com.mysql.cj.jdbc.Driver"); return DriverManager.getConnection("jdbc:mysql:///mybatis", "root", "123456"); } catch (Exception exception) { return null; } }
3、测试
@Test public void test1(){ ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class); Connection connection = (Connection) ac.getBean("connection"); System.out.println(connection); }
4.10 @Import
作用:如果配置过多,会有多个配置类,该注解可以为主配置类导入其他配置类
位置:主配置类上方
// Jdbc配置类 @Configuration public class JdbcConfig { @Bean(name = "connection") public Connection getConnection(){ try { Class.forName("com.mysql.cj.jdbc.Driver"); return DriverManager.getConnection("jdbc:mysql:///mybatis", "root", "123456"); } catch (Exception exception) { return null; } } } // 主配置类 @Configuration @ComponentScan("com.zj") @Import(JdbcConfig.class) public class SpringConfig { }
五、Spring整合MyBatis
我们知道使用MyBatis时需要写大量创建SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession等对象的代码,而Spring的作用是帮助我们创建和管理对象,所以我们可以使用Spring整合MyBatis,简化MyBatis开发。
5.1 创建maven项目,引入依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>springandmybatis</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!--Mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.7</version> </dependency> <!--JDBC驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.26</version> </dependency> <!--Druid连接池--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.16</version> </dependency> <!--Spring--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.13</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.3.13</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.13</version> </dependency> <!-- MyBatis与Spring的整合包,该包可以让Spring创建MyBatis的对象 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.6</version> </dependency> <!--单元测试--> <!-- junit,如果Spring5整合junit,则junit版本至少在4.12以上 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!-- spring整合测试模块 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.3.13</version> </dependency> </dependencies> </project>
5.2 编写配置文件
1、编写数据库配置文件db.properties
jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql:///mybatis jdbc.username=root jdbc.password=123456
2、创建MyBatis配置文件SqlMapConfig.xml,数据源、扫描接口都交由Spring管理,不需要在MyBatis配置文件中设置。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> </configuration>
3、创建Spring配置文件applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" 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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--扫描包--> <context:component-scan base-package="com.zj"/> <!--读取配置文件--> <context:property-placeholder location="classpath:db.properties"/> <!--配置数据源--> <bean id="datasource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <!--创建Spring封装过的SqlSessionFactory对象--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="datasource"/> </bean> <!--创建Spring封装的SqlSession--> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean> <!-- 该对象可以自动扫描持久层接口,并为接口创建代理对象 --> <bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- 配置扫描的接口包 --> <property name="basePackage" value="com.zj.dao"/> </bean> </beans>
5.3 准备实体类
public class Student { private int id; private String name; private String sex; private String address; // 省略构造方法/getter/setter/tostring }
5.4 编写持久层接口
@Repository public interface StudentDao { // 查询所有学生 @Select("select * from student") List<Student> findAll(); // 添加学生 @Insert("insert into student values(null,#{name},#{sex},#{address})") void add(Student student); }
5.5 编写service类
package com.zj.service; import com.zj.dao.StudentDao; import com.zj.pojo.Student; import org.mybatis.spring.SqlSessionTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; import java.util.List; @Service public class StudentServiceImpl implements StudentService { /*直接使用代理对象*/ @Autowired private StudentDao studentDao; @Override public List<Student> findAllStudents() { return studentDao.findAllStudents(); } }
5.6 测试
import com.zj.pojo.Student; import com.zj.service.StudentService; 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 java.util.List; //junit使用spring的方式运行代码,即自动获取spring容器 @RunWith(SpringJUnit4ClassRunner.class) //spring容器创建时读取的配置文件 @ContextConfiguration(locations = "classpath:applicationContext.xml") //配置类的写法 //@ContextConfiguration(classes = config.class) public class TestStudentService { @Autowired private StudentService studentService; @Test public void testFindAllStudent() { List<Student> allStudents = studentService.findAllStudents(); for (Student student : allStudents) { System.out.println(student); } } }
六、SpringAOP
AOP的全称是Aspect Oriented Programming,即面向切面编程。是实现功能统一维护的一种技术,它将业务逻辑的各个部分进行隔离,使开发人员在编写业务逻辑时可以专心于核心业务,从而提高了开发效率。
- 作用:在不修改源码的基础上,对已有方法进行增强。
- 实现原理:动态代理技术。
- 优势:减少重复代码、提高开发效率、维护方便
- 应用场景:事务处理、日志管理、权限控制、异常处理等方面。
6.1 AOP相关术语
为了更好地理解AOP,就需要对AOP的相关术语有一些了解
名称 | 说明 |
Joinpoint(连接点) | 指能被拦截到的点,在Spring中只有方法能被拦截。(spirng中所有方法都能被称为连接点) |
Pointcut(切点) | 指要对哪些连接点进行拦截,即被增强的方法。(只有真正被增强的方法才叫切点) |
Advice(通知) | 指拦截后要做的事情,即切点被拦截后执行的方法 。 |
Aspect(切面) | 切点+通知称为切面 |
Target(目标) | 被代理的对象 |
Proxy(代理) | 代理对象 |
Weaving(织入) | 生成代理对象的过程 |
6.2 AOP入门
AspectJ是一个基于Java语言的AOP框架,在Spring框架中建议使用AspectJ实现AOP。
接下来我们写一个AOP入门案例:dao层的每个方法结束后都可以打印一条日志:
1、引入依赖
<!-- spring --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.13</version> </dependency> <!-- AspectJ --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.7</version> </dependency> <!-- junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency>
2、编写通知类
package com.zj.advice; //通知类 public class MyAdviceAspect { //后置通知 public void AfterReturningMethod(JoinPoint joinPoint){ System.out.println("切点方法名:"+joinPoint.getSignature().getName()); System.out.println("哪个类执行的该方法:"+joinPoint.getTarget()); System.out.println("打印日志……"); } }
3、配置切面(将切点和通知匹配到一起形成切面)
<!--通知对象--> <bean id="myAdviceAspect" class="com.zj.advice.MyAdviceAspect"/> <!--配置AOP--> <aop:config > <!--配置切面(切点+通知)--> <aop:aspect ref="myAdviceAspect"> <!--配置切点;execution表达式的意思是StudentDao类下的所有方法都会被拦截--> <aop:pointcut id="myPoint" expression=" execution(* com.zj.dao.StudentDao.*(..)) "/> <!--配置后置通知--> <aop:after-returning method="AfterMethod" pointcut-ref="myPoint"/> </aop:aspect> </aop:config>
4、测试
6.3 通知类型
AOP有以下几种常用的通知类型:
通知类型 | 描述 |
前置通知 | 在方法执行前添加功能 |
后置通知 | 在方法正常执行后添加功能 |
异常通知 | 在方法抛出异常后添加功能 |
最终通知 | 无论方法是否抛出异常,都会执行该通知 |
环绕通知 | 在方法执行前后添加功能 |
package com.zj.advice; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; //通知类 public class MyAdviceAspect { //后置通知 public void AfterReturningMethod(JoinPoint joinPoint){ System.out.println("切点方法名:"+joinPoint.getSignature().getName()); System.out.println("哪个类执行的该方法:"+joinPoint.getTarget()); System.out.println("打印日志……"); } //前置通知 public void BeforeMethod(JoinPoint joinPoint){ System.out.println("前置通知……"); } //异常通知 public void AfterThrowingMethod(Exception exception){ System.out.println("异常:"+exception.getMessage()); System.out.println("异常通知……"); } //最终通知 public void AfterMethod(JoinPoint joinPoint){ System.out.println("最终通知……"); } //环绕通知 public Object RoundMethod(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("环绕前……"); Object proceed = proceedingJoinPoint.proceed();//执行原方法 System.out.println("环绕后……"); return proceed; } }
<!--通知对象--> <bean id="myAdviceAspect" class="com.zj.advice.MyAdviceAspect"/> <!--配置AOP--> <aop:config > <!--配置切面(切点+通知)--> <aop:aspect ref="myAdviceAspect"> <!--配置切点;execution表达式的意思是StudentDao类下的所有方法都会被拦截--> <aop:pointcut id="myPoint" expression=" execution(* com.zj.dao.StudentDao.*(..)) "/> <!--前置通知--> <aop:before method="BeforeMethod" pointcut-ref="myPoint"/> <!--配置后置通知--> <aop:after-returning method="AfterReturningMethod" pointcut-ref="myPoint"/> <!--异常通知--> <aop:after-throwing method="AfterThrowingMethod" pointcut-ref="myPoint" throwing="exception"/> <!--最终通知--> <aop:after method="AfterMethod" pointcut-ref="myPoint"/> <!--环绕通知--> <aop:around method="RoundMethod" pointcut-ref="myPoint"/> </aop:aspect> </aop:config>
6.4 切点表达式
使用AspectJ需要使用切点表达式配置切点位置,写法如下:
- 标准写法:访问修饰符 返回值 包名.类名.方法名(参数列表)
- 访问修饰符可以省略。
- 返回值使用
*
代表任意类型。 - 包名使用
*
表示任意包,多级包结构要写多个*
,使用*..
表示任意包结构 - 类名和方法名都可以用
*
实现通配。 - 参数列表
- 基本数据类型直接写类型
- 引用类型写
包名.类名
*
表示匹配一个任意类型参数..
表示匹配任意类型任意个数的参数
- 全通配:
* *..*.*(..)