SpringAOP,IOC, DI

本文详细介绍MyBatis与Spring框架的整合过程,包括XML管理SQL、接口注解管理SQL、声明式事务管理、反射知识、AOP面向切面编程等内容,深入探讨了如何在Spring环境中高效使用MyBatis进行数据库操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  1. 和mybatis整合
    XML 管理sql --> xml 映射
    用接口配合注解 管理SQL --> 接口映射

@Insert(“sql”)
@Update(“sql”)
@Delete(“sql”)
@Select(“sql”)

@Insert(“insert into user(username,password) values(#{属性名}, #{属性名})”)
public void insert(User user);

  1. 接口mapper的名称 要和 xml 文件名称一致
    src/java/UserMapper.java
    src/resources/UserMapper.xml
  2. 包名和接口类名要与 xml namespace
  3. 接口的方法名对应 xml中的id
  4. 接口方法名不能重复
  1. 声明式事务管理
<tx:annotation-driven/>  启用声明式事务管理和 @Transactional 

配置事务管理器, 完成事务的具体操作(开始事务,提交,操作)

<bean id="transationManager" class="DataSourceTransationManager">
   <property name="dataSource" ref="连接池的id"/>
</bean>

把@Transactional注解加在需要事务控制方法上
方法正常,事务提交
方法出现运行时异常,事务回滚
方法出现了Exception或子类(检查异常)事务仍提交
@Transactional(rollbackFor=异常.class) 控制哪类异常回滚

@Transactional(noRollbackFor=异常.class) 控制哪类异常不回滚
@Transactional(timeout=超时时间) 超过了时间,事务回滚
@Transactional(readOnly=true|false) 如果事务内仅有查询,设为readOnly=true能够提高一些效率
@Transactional(isolation=隔离级别) 脏读,不可重复读,幻读
mysql默认 (可重复读隔离级别)能够避免脏读,不可重复读,部分幻读
oracle默认 (提交读) 只能避免脏读
@Transactional(propagation=传播行为)
(默认)required 必须的(必须有一个事务,原来没有,开始新的;原来有,就用原来的)
supports 支持的(可选的,原来没有,不用新的;原来有,就用原来的)
requries_new 需要新的, 每次执行此方法时都开始新事务

反射知识

  1. 用另外的手段来创建对象、调用对象方法、使用对象的属性,常用在框架编程

  2. 获取类对象(*)
    Class<?> c = Class.forName(“包.类”); // 不需要事先import
    Class<?> c = 类.class; // 需要事先import

  3. 创建对象实例(*)
    c.newInstance(); // 调用无参构造,创建对象
    c.getConstructor(构造方法的参数类型).newInstance(构造方法参数)

  4. 获取方法(*)
    c.getMethods(); // 得到的是本类以及继承的所有public的方法
    c.getDeclaredMethods(); // 得到本类的所有方法(不考虑访问修饰符)
    c.getMethod(方法名字, 方法的参数类型…)
    c.getDeclaredMethod(方法名字, 方法的参数类型…)

  5. 获取属性
    c.getFields(); // 得到的是本类以及继承的所有public的属性
    c.getDeclaredFields(); // 得到本类的所有属性
    c.getField(“属性名”);
    c.getDeclaredField(“属性名”);

  6. 方法对象 Method(*)
    对象.方法名(参数); // 正常调用方法
    方法.invoke(对象, 参数); // 反射调用方法

  7. 属性对象 Field
    对象.属性 = 值; // 正常赋值
    对象.属性; // 正常取值

    属性.set(对象, 值); // 反射赋值
    属性.get(对象); // 反射取值

  8. 突破private的限制
    方法对象 Method.setAccessible(true); // 允许私有方法可以访问
    属性对象 Field.setAccessible(true); // 允许私有属性可以访问

=========================================================================

1.实现事务代码重用

思路:
1) 提供了一个代理类(Proxy)
调用通知类的invoke方法
获取方法对象和方法实际参数
与目标要实现相同的接口(目的是让使用者察觉不出是代理替换了原来的目标)
2) 提供了一个通知类(Advice)
实现了重复代码(事务的重复代码)
反射调用了目标对象的方法
把重复代码和目标方法联系在了一起

jdk提供了一个通用的接口 InvocationHandler (通知接口)

2.动态代理

刚才自己实现的代理类称之为静态代理, jdk 还提供了动态代理技术, 它是指在程序运行期间,
由jdk生成这个代理类和他的实例对象.

正常使用类:*.java -> javac -> .class -> java -> 加载该class到虚拟机
动态代理 直接生成了
.class字节码, 加载该class到虚拟机

Proxy.newProxyInstance(类加载器, 要实现的接口数组, InvocationHandler);

3. spring的aop

3.1 pom.xml 新增依赖

<!-- spring aop -->
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-aop</artifactId>
	<version>4.3.17.RELEASE</version>
</dependency>

<!-- 第三方 aop依赖 aspectJ -->
<dependency>
	<groupId>org.aspectj</groupId>
	<artifactId>aspectjweaver</artifactId>
	<version>1.8.9</version>
</dependency>
<dependency>
	<groupId>org.aspectj</groupId>
	<artifactId>aspectjrt</artifactId>
	<version>1.8.9</version>
</dependency>

3.2 在spring的配置文件中加入aop的相关标签

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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
 http://www.springframework.org/schema/tx
 http://www.springframework.org/schema/tx/spring-tx.xsd
 http://www.springframework.org/schema/aop
 http://www.springframework.org/schema/aop/spring-aop.xsd">

	<!-- 目标类 -->
    <bean id="userService" class="spring.service.UserServiceTarget">
    </bean>

    <!-- 通知类 -->
    <bean id="transactionAdvice" class="spring.advice.TransactionAdvice">
    </bean>

    <!-- 让相关注解生效, 还能够帮我们生成底层的代理对象 -->
    <aop:aspectj-autoproxy/>
</beans>

3.3 实现通知类

// 切面
@Aspect
public class TransactionAdvice {

    @Around("within(spring.service.UserServiceTarget)")
    // 通过切点表达式,把通知和目标结合在一起
    // 作用当UserServiceTarget类中任意一个方法执行时(biz1,biz2) 就会和下面的transaction方法结合在一起了


    // 通知类型:环绕通知,决定了这个通知方法会和哪些目标方法结合
    // 这个方法称为通知方法,它其中包含了一些重复逻辑,另外它负责调用目标方法
    public Object transaction(ProceedingJoinPoint pjp) { // pjp 内部去调用目标的方法
        System.out.println("开始事务");
        Object r = null;
        try {
            // 调用目标
            r = pjp.proceed(); // 目标  biz1,  biz2...

            System.out.println("提交事务");
        } catch (Throwable e) {
            System.out.println("回滚事务");
        }
        return r;
    }
}

3.4 使用

使用上,尽量实现了无侵入的效果,原来的代码不受影响

ClassPathXmlApplicationContext context
		= new ClassPathXmlApplicationContext("spring.xml");

// getBean方法返回的不是target对象,而是由spring容器生成的代理对象,底层产生代理的技术就是:jdk动态代理技术
UserService service = context.getBean(UserService.class);

service.biz1();
// 代理对象内部调用了通知TransactionAdvice对象, 检查是否和通知方法上的切点表达式符合
// 如果符合,就执行该通知方法, 通知方法内部再调用目标

System.out.println("service真正类型是:"+service.getClass());

aop 的概念

代理 proxy
目标 target 被代理的对象
通知 advice 包含了可重用代码(例如事务代码),并调用目标
切点 pointcut 把通知和目标结合一起,它定义的是一种匹配规则
切面 aspect 切面=通知+切点

aop (aspect切面 oriented 面向 programming 编程) 面向切面编程
oop 面向对象编程

切点 pointcut

aspectj中定义的一些切点表达式

  • within(包名.类名) 这个类中所有方法执行时,都会应用通知方法
  • execution(访问修饰符 返回值类型 包名.类名.方法名( 参数类型… ))
    注意* 可以匹配任意类型, 可以出现在方法返回值,方法名,类名当中
    注意.. 可以匹配方法参数,表示参数的个数和类型都是任意的
  • @annotation(包名.注解名)
    它是根据方法上的注解进行匹配, 如果有注解则匹配

通知类型

环绕通知: @Around(切点表达式) 加在通知方法上(功能最全的), 通知方法环绕着目标方法
前置通知: @Before 通知代码只会出现在目标方法之前
正常结束通知: @AfterReturning 在目标方法正常结束后,调用通知方法
异常通知: @AfterThrowing 在目标方法出现异常后,调用通知方法
结束通知: @After 在目标方法结束后(正常或异常),总会调用的通知

统计每个业务方法的执行时间

调用流程

容器启动时

  1. spring会检查容器,看是否需要为这些bean创建代理对象
  2. 检查所有的切点表达式,如果匹配到了目标,就为该目标创建代理对象

获取对象时(getBean, 依赖注入)
会检查这个类型是否有代理对象,如果有,优先返回代理对象

调用方法时
调用了代理对象的方法,代理对象会首先经过通知类(多个)

检查切点,如果切点匹配,再进行下面的通知调用

根据不同的通知类型进行调用:
例如前置通知先调通知,再调目标
如果是环绕通知,先调用通知,通知内部调用目标

概念

面向切面编程:就是把重复的逻辑抽取为一个通知方法,
然后通过切点匹配来决定哪些目标要应用这些通知。
其中利用了代理的技术,在代理中检查切点是否匹配,调用通知(多个),最后调用目标

生成代理的常见技术

jdk的动态代理, 只能针对接口实现代理, jdk动态代理在高版本中性能优于cglib
cglib动态代理, 既可以针对接口实现代理,也可以生成子类充当目标父类的代理

spring默认使用jdk动态代理,如果没有实现接口,就使用cglib代理

回顾

  1. 代理技术
    Proxy.newProxyInstance(类加载器, 接口数组, InvocationHandler);

// 代理类中任何方法调用,都会进入InvocationHandler的invoke方法
class 通知类 implements InvocationHandler {
public Object invoke(代理对象, 方法对象, 方法实际参数数组){
// 事务开始
反射调用目标方法
// 事务提交,回滚
}
}
// 代码得到重用

  1. spring的aop
    代理proxy
    通知advice
    切点pointcut
    目标target
    切面aspect = 通知+切点

@Aspect
public class 切面类(通知类) {
@Around(“切点表达式–匹配哪些目标方法要执行下面的通知”)
public Object 通知方法名(ProceedingJoinPoint pjp) {
重复逻辑(例如事务、统计运行时间、权限控制…);
if(权限检查通过)
return pjp.proceed(); // 执行了目标方法
else {
throw 没有权限;
}
}
}

在配置文件中启用aop编程 <aop:aspectj-autoproxy/>

  1. 切点表达式
    within(包名.类名) 匹配此类中所有的方法
    execution(public 返回类型 包名.类名.方法名(参数类型)) 匹配方法的各个要素

    • 表示匹配任意的返回类型、类名、方法名…
      … 加在方法参数上,表示参数的类型、个数任意
      @annotation(包名.注解类名)
      匹配方法上是否有这个注解
  2. 通知类型
    @Around 环绕通知
    @Before 前置通知
    @After 最终通知
    @AfterReturning 正常结束通知
    @AfterThrowing 异常通知

  3. 代理创建方式
    jdk动态代理 – 限制:只能针对接口生成代理
    cglib代理 – 既可以针对接口生成代理,也可以生成一个子类充当代理
    spring会优先使用jdk动态代理, 不能用jdk动态代理的,再用cglib

===============================================================

spring-mvc

基于spring的, ioc控制反转, di依赖注入, aop面向切面编程

model 模型 - 数据和操作数据的逻辑(狭义的就是数据) 包括了实体类和业务类(例如 User,UserService)
view 视图 - 数据展现, 包括(jsp, jstl, el)
controller 控制器 把模型和视图关联在一起, 包括servlet

让程序的各个部分分工清晰,各司其职。让程序的可维护性提高。

使用步骤:

  1. 在pom.xml文件中添加 spring-webmvc等6个依赖
    注意pom.xml文件中需要添加<packaging>war</packaging> (决定了是maven的web项目)
    然后手动补充一个src/main/webapp目录, src/main/webapp/WEB-INF/web.xml
    在配置tomcat时选择 项目名:war exploded

  2. 在spring-mvc.xml中添加mvc的xml命名空间

  3. 前控制器 – 统一的入口
    它是由spring-mvc 提供的的一个servlet: DispatcherServlet
    在web.xml 对它进行配置, maven 的目录结构中,该文件的路径是 src/main/webapp/WEB-INF/web.xml

<!-- 职责: 
   1.作为统一入口
   2.创建spring容器
   3.在tomcat启动时,就把spring容器创建好
-->
<servlet>
	<servlet-name>springmvc</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<!-- 指明了spring配置文件的位置 -->
	<init-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring-mvc.xml</param-value>
	</init-param>
	<!-- 在tomcat启动时,就创建这个servlet,并初始化该servlet -->
	<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
	<servlet-name>springmvc</servlet-name>
	<url-pattern>/</url-pattern> 
	<!-- 给 DispatcherServlet 指定路径 
	假设浏览器  /hello  没有,会找 / 这个路径
			   /s1     没有,会找 / 这个路径
			   
			 只要没有其他servlet能够精确匹配这个请求路径,这个请求都会被 / 的这个servlet来处理
	-->
</servlet-mapping>
  1. 控制器
    @Controller
    public class 控制器类{

    @RequestMapping("/请求路径")
    public String 控制器方法(){

    }
    }
    // 控制器类要交给spring容器管理
    并spring-mvc中加入:<mvc:annotation-driven/> 来启用mvc的相关功能

  2. 处理视图
    控制器方法的返回值是视图名
    前缀+视图名+后缀就构成了最终jsp视图路径, 控制器会根据视图路径用请求转发跳转过去

<mvc:view-resolvers>
	<mvc:jsp prefix="/" suffix=".jsp"/>
</mvc:view-resolvers>
  1. 请求被处理的流程

    1. 请求从浏览器发送到tomcat, 地址为:http://localhost:8080/hello
    2. tomcat先将该路径与servlet进行匹配,结果没有精确的地址与之匹配,就找到了 / 这个地址
      / 地址对应的是DispatcherServlet,由它来处理请求
    3. DispatcherServlet 再到spring容器中找控制器类
      把每个控制器中带有@RequestMapping的方法路径与请求路径进行匹配,
    4. 匹配到了就执行该方法, 如果匹配不到报404错误
    5. 根据方法的返回结果找到jsp视图
    6. 由jsp视图生成html代码作为响应返回给浏览器
  2. 以maven插件方式运行tomcat
    在pom.xml中加入插件信息:

<plugin> <!-- 配置一个内嵌的tomcat -->
	<groupId>org.apache.tomcat.maven</groupId>
	<artifactId>tomcat7-maven-plugin</artifactId>
	<version>2.2</version>
	<configuration>
		<port>8080</port> <!-- 端口号 -->
		<path>/</path> <!-- 项目在tomcat中的访问路径 -->
		<uriEncoding>utf-8</uriEncoding> <!-- 设置get请求中解码的字符集,解决get请求中的中文乱码问题 -->
	</configuration>
</plugin>

然后在右侧的maven菜单中找到tomcat7插件,运行tomcat7:run这个命令

  1. 接收请求参数
  1. 把方法参数和请求参数直接对应

  2. 写一个实体类型,把实体的属性与请求参数对应

  3. 路径参数(把参数值不是写在?之后,而是包含在路径当中)
    /deleteUser?id=1 传统写法
    /deleteUser/1 路径参数 要删除1号用户
    /deleteUser/2 要删除2号用户

    路径参数可以通过@PathVariable 与方法参数一一对应

  1. 使用模型数据
    Model 接口, 代表了模型数据, 它也是加在控制器方法之上 可以直接使用它的实现类ModelMap或Map
    向模型添加数据: model.addAttribute(“变量名”, 值);
    model中的数据在转发之前,都会被存入request作用域

springmvc中
常见错误:
400 错误:发生在请求参数赋值给方法参数时,发生了类型转换问题
例如:字符串转整数
字符串转日期 对于日期需要加 @DateTimeFormat(pattern=“日期格式”)
中文乱码问题:
可以配置spring提供的字符编码过滤器

<!-- spring 提供的字符编码过滤器 -->
<filter>
	<filter-name>encoding</filter-name>
	<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
	<init-param>
		<param-name>encoding</param-name>
		<param-value>utf-8</param-value>
	</init-param>
</filter>

<filter-mapping>
	<filter-name>encoding</filter-name>
	<url-pattern>/ *</url-pattern>
</filter-mapping>
静态资源的 404 错误
	静态资源是指 图片、css、html、js
	原因是DispatcherServlet的路径是/ 跟tomcat中处理图片的Servlet路径冲突了
	所以所有图片的请求被DispatcherServlet所处理,把图片的路径当做了控制器路径
解决办法:	
	在spring配置文件中加入:`<mvc:default-servlet-handler/>`

让tomcat能够热部署,让类的修改不用重启服务器就可以生效

  1. 首先保证 打包方式是 war exploded
  2. 其次,启动tomcat使用debug方式启动
  3. 在tomcat设置时,on frame deactivation 选中 update classes and resources 选项
    当光标从idea切换至浏览器时,就会更新修改
    限制:只能针对当前存在的类生效,如果是新增的类检测不到
    配置文件的改动检测不到
  1. json格式的响应
    js 代表javascript object 对象, notation 标记

之前返回的大部分响应类型 text/html
json也是一种响应格式:也是文本格式的,更类似于javascript的对象写法
{
“username”:“张三”,
“age”:18
}
如果转换json时出现了错误: No converter found for return value of type:
是因为json转换的jar包没有加, 可以在pom文件中加入相应的依赖:

<dependency>
	<groupId>com.fasterxml.jackson.core</groupId>
	<artifactId>jackson-databind</artifactId>
	<version>2.9.1</version>
</dependency>  

在控制器方法上添加@ResponseBody注解,它可以把方法的返回值转换成json字符串

java中 map以及实体类 会被转换为json中的对象 {属性名:属性值}
java中 list或数组 会被转换为json中的数组 [元素1, 元素2, …]

jackson常见注解:
@JsonFormat 可以对日期类型进行格式化
@JsonFormat(pattern = “yyyy-MM-dd HH:mm:ss”, timezone = “GMT+8”) 注意加8小时才是北京时间
@JsonIgnore 可以在转换时忽略加了@JsonIgnore的属性
@JsonProperty(“新的属性名”) 可以在转换时改变属性的名字

  1. 把springmvc的控制器和之前的 service, mapper 结合起来
    UserController
    @Autowired
    private UserService service;
    UserService
    @Autowired
    private UserMapper mapper;
    UserMapper 接口
    @Insert
    public void insert(User user);

做一个查询所有用户的例子,把流程调通.

  1. 用aop的思想完成方法的记时
    UserService
    foo 要求记时
    bar
    insert 要求记时
    update
    如何完成某几个方法的记时
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

phial03

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值