Spring
1、动态代理详解
动态代理实现的步骤
- 创建接口,定义目标要完成的功能
- 创建目标类,实现接口
- 创建InvocationHandler接口实现类 ,在invoke方法中完成代理的功能
- 调用目标方法
- 增强的功能
- 使用Proxy类创建代理类,并把返回值转换成接口。
1、创建接口,定义目标要完成的功能
package com.jfs.proxys.jdkproxy;
public interface Rent {
void sell();
void buy();
}
2、创建目标类,实现接口
package com.jfs.proxys.jdkproxy;
public class Host implements Rent {
@Override
public void sell() {
System.out.println("卖家:卖房子");
}
@Override
public void buy() {
System.out.println("目标类:买房子");
}
}
3、创建InvocationHandler接口实现类 ,在invoke方法中完成代理的功能
package com.jfs.proxys.jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvocationHandle implements InvocationHandler {
private Object rent;
public ProxyInvocationHandle(Object rent) {
this.rent = rent;
}
//proxy是代理对象,method是被代理的方法, args是方法的参数,这些都不要手动输入,jdk自动注入
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//增强的功能
System.out.println("执行了"+method.getName());
//需要完成的目标类功能
Object res = method.invoke(rent, args);
return res;
}
}
4、通过Proxy类获取代理类对象,转换为相应接口的类型(可以代理接口中的任意方法)
package com.jfs.proxys.jdkproxy;
import java.lang.reflect.Proxy;
public class ProxyTest {
public static void main(String[] args) {
Rent rent = new Host();
ProxyInvocationHandle handle = new ProxyInvocationHandle(rent);
//第一个参数是目标类的类加载器,第二个参数是目标类实现的接口,第三个参数是InvocationHandler接口实现类
Rent proxy = (Rent) Proxy.newProxyInstance(rent.getClass().getClassLoader(), rent.getClass().getInterfaces(), handle);
proxy.hashCode();
proxy.sell();
proxy.buy();
}
提示: 执行proxy.sell方法执行InvocationHandler类的invoke()方法 参数method就是sell()方法
2、Aop
2.1 AOP概念
面向切面编程,是基于动态代理实现的,动态代理有jdk动态代理、cglib动态代理
aop就是把动态代理的步骤规范化,让开发人员使用统一的方式去开发。
- 分析去哪些功能需要提取出来,找出切面
- 确定切面执行的时间,是在方法钱还是在方法后等等
- 确定切面在哪个类,哪个方法添加增强功能
2.2 AOP术语
-
Aspect:切面,表示增强的功能,常见的切面有日志、事务、参数检查、权限验证等等。
-
Joinpoint:连接点 表示业务方法和切面的位置,某类中的业务方法 。
-
PointCut:切入点 表示一个或者几个连接点的汇总。
- advice:通知 :表示切面执行的时间。
2.3 实现AOP的框架
-
spring:通常在事务中用的比较多,spring中的aop使用起来比较笨重。
-
aspectJ:一个专门做aop的开源框架,spring集成了aspectJ的所有功能
aspectJ实现aop的方式
-
通过xml注解方式
-
通过注解的方式
-
2.4 切入点表达式
execution(访问权限 返回值类型 方法的声明(参数列表) 异常类型)
其中:加粗的字体不能省略
示例
execution(public * *(..)):任意的类型为public的方法
execution(* set*(..)) :任意一个以set方法开头的方法
execution(* com.xyz.service. *. *(..)):com.xyz.service包下面的任意类的任意方法
execution(* com.xyz.service..*. *(..))service包及其子包下所有的方法
execution(* ..service.*. *(..))所有包中service包中的所有类和方法
*代表0个或者多个字符
..用在方法参数中代表是任意个参数,用在包后面表示当前包和所有子包
2.5 AspectJ(aop)使用步骤
- 创建一个maven项目
- 导入依赖
- spring 依赖
- aspectj依赖
- 创建目标类和其接口
- 创建切面类
- 在切面类上加@Aspect
- 在方法上面加通知注解,例如@Before,有需要切入点的表达式execution()
- 创建spring配置文件,声明对象,把对象统一交给spring容器管理
- 声明目标对象
- 申明切面对象
- 生产aspectj框架中自动代理生成器标签
- 创建测试类,通过spring容器获得目标对象,通过代理执行方法,实现aop增强功能。
注意:代理对象实际上目标对象修改后的
什么时候考虑使用AOP
- 需要增加功能,同时又不能给变原来代码的情况下。
- 有很多类需要增强相似的功能,通常有给业务方法增加事务,日志等功能。
2.6 Aop通知类型及其分类
-
前置通知(Before)
/** * 前置通知 * public 修饰权限 * 返回值只能为void * 方法名自定义 * 参数可以没有,也可以有 * 如果有参数,不是自定义的,有几个参数可以用 */ /** * 参数值:joinPoint ,为了获得目标方法的信息。 * 参数只能在第一个位置 */ @Before(value = "execution( * *.doSome(..))") public void before(JoinPoint joinPoint){ }
-
后置通知
/** * 后置通知方法 * 公共方法public * 方法没有返回值 * 方法名自定义 * 方法有参数,类型推荐Object */ /** * @AfterReturning:后置通知 * 属性:1、value:切入点表达式 * 2、returning:自定义变量,表示目标方法的返回值,要和通知方法的参数名一致 * 特点:1、在目标方法后执行 * 2、能够获取方法的返回值 */ @AfterReturning(value = "execution(* *..*.dother(..))",returning = "obj" ) public void after(Object obj){ System.out.println("目标方法的后置通知"); System.out.println("方法的返回值是:"+obj); Integer obj1 = (Integer) obj; obj =obj1+2; System.out.println("after通知中obj的值"+obj); }
-
环绕通知
/** * 环绕通知定义格式 * public 类型 * 有返回值,推荐为Object * 方法名自定义 * 参数类型是ProceedingJoinPoint 类似InvocationHandler中invoke方法的Method参数。 继承JoinPoint * 返回值是目标方法的返回值可以被修改 * * 环绕通知经常做事务,目标方法前开启事务,方法后提交事务 */ /** * @Around * 1、参数:value(切入点表达式) * 2、方法前后都能增强功能 * 3、能够控制方法是否能够被执行 * 4、修改目标方法的返回值 */ @Around(value = "execution(* *..*.doSome3(..))") public Object myAround(ProceedingJoinPoint pjp) throws Throwable { String name=null; System.out.println("环绕通知前置通知"+new Date()); //控制方法能够被执行 Object[] args = pjp.getArgs(); if (args!=null &&args.length>0){ if ("张三".equals(args[0])){ name= (String) pjp.proceed(); } } System.out.println("环绕通知目标方法后面的方法"); return name+"的儿子"; }
-
异常通知
/** * 异常通知 * public修饰 * 返回值为void * 方法名自定义 * 参数有一个Exception 也可以加一个JoinPoint */ /** * @AfterThrowing * 属性:value:切入点 * throwing 必须和参数名一样 * 特点: * 在有异常时执行 * 可以作为异常的监控程序 */ @AfterThrowing(value = "execution(* *..*.doSome4(..))",throwing = "exception") public void myThrow(Exception exception){ System.out.println("发生了"+exception.getMessage()+",发一个短信给主机"); }
-
最终通知
/** * 最终通知 * public修饰 * 返回值为void * 方法名自定义 * 参数可以有,也可以没有,有的话就是JoinPoint */ /** * @After(value="execution()") * 总是会执行 * 方法执行执行 * 一般做资源清除功能 */ @After(value = "execution(* *..*.doSome*(..))") public void after(){ System.out.println("最终通知"); }
3、Spring整合mybatis
3.1、Spring整合mybatis实现步骤
主要实现:
- 创建druid连接池对象
- 创建SqlSessionFactory对象
- 创建SqlSession对象
- 创建Dao对象
实现步骤
-
导入依赖
- spring
- mybatis
- mysql
- jdbc
- spring-mybatis
- aop(aspectj)
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.6</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.0.11.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.7</version> </dependency> </dependencies>
-
编写配置文件
-
配置数据源
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://47.102.110.12:3306/mybatistest?useSSL=false"/> <property name="username" value="root"/> <property name="password" value="eb7ecd2c8942412d"/> </bean>
-
注册SqlSessionFactory及SqlSessionTemplate(和sqlSession功能一样)
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 绑定mybatis的配置文件 --> <property name="configLocation" value="classpath:mybatis-config.xml"/> <property name="dataSource" ref="dataSource"/> </bean> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean>
-
将SqlSession注册到实现类中,调用Mapper进行数据库操作
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; import java.util.List; @Component public class ServiceImpl { @Autowired private SqlSessionTemplate sqlSession; public List<User> findUsers(){ UserMapper mapper = sqlSession.getMapper(UserMapper.class); return mapper.selectUsers(); } }
-
测试
public class MyTest { @Test public void test01(){ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("springApplicationConfig.xml"); ServiceImpl serviceImpl = (ServiceImpl) context.getBean("serviceImpl"); List<User> users = serviceImpl.findUsers(); for (User user : users) { System.out.println(user); } } }
-
-
测试
3.2、mybatis实现步骤
mybatis官方文档地址:
https://mybatis.org/mybatis-3/zh/configuration.html#mappers
-
编写实体类
-
编写接口
-
编写核心配置文件
<?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> <!-- 属性配置,属性可以在xml配置文件中使用 --> <properties resource="db.properties"> <property name="username" value="jfs"/> <property name="password" value="123"/> </properties> <!-- mybatis的参数设置 --> <settings> <setting name="logImpl" value="LOG4J"/> <setting name="mapUnderscoreToCamelCase" value="true"/> <setting name="cacheEnabled" value="true"/> </settings> <!-- 为包下的类起别名,为类名的首字母小写 --> <typeAliases> <package name="com.jfs.pojo"/> </typeAliases> <!-- 环境配置,可以配置mysql或者oracle --> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <!-- 将包内的映射器接口实现全部注册为映射器 --> <mappers> <package name="com.jfs.dao"/> </mappers> </configuration>
-
编写Mapper.xml映射文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.jfs.dao.TeacherMapper"> <select id="findTeacherById" resultMap="findTeacher"> select s.id s_id, s.name s_name ,s.age s_age,t.id t_id ,t.name t_name ,t.age t_age from student s left join stu_teacher st on s.id =st.stu_id left join teacher t on st.teacher_id=t.id where t.id =#{id} </select> <resultMap id="findTeacher" type="teacher"> <id property="id" column="t_id"/> <result property="name" column="t_name"/> <result property="age" column="t_age"/> <collection property="students" ofType="student"> <id property="id" column="s_id"/> <result property="name" column="s_name"/> <result property="age" column="s_age"/> </collection> </resultMap> </mapper>
通常mapper.xml映射文件放在resources文件下和接口相同的名字的包下面,例如接口在cpm.jfs.dao包下
则Mapper.xml文件在resources文件包下面新建一个com.jfs.dao包,然后放在这下面。
-
测试
public void test01() throws IOException { String xml ="mybatis-config.xml"; InputStream resource = Resources.getResourceAsStream(xml); SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(resource); //true代表的是开启事务 SqlSession sqlSession = sessionFactory.openSession(true); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> users = mapper.selectUsers(); for (User user : users) { System.out.println(user); } }
3.3、mybatis-spring
3.3.1、官方文档地址
http://mybatis.org/spring/zh/index.html
4、声明式事务
4.1、回顾事务
把一组业务当做一个业务来做,要么都成功,要么都失败
4.2、事务的ACID原则
- 原子性
- 一致性
- 持久性
- 隔离性
5、Spring事务管理
- 声明式事务:AOP
- 编程式事务:在代码中进行事务管理
5.1、声明式事务配置
5.1.1、事务的隔离级别
- 读未提交
- 读已提交
- 可重复读
- 串行化
5.1.2、事务的超时时间
5.1.3、事务的传播行为
-
PROPAGATION_REQUIRED
-
PROPAGATION_SUPPORTS
-
PROPAGATION_NEW
-
PROPAGATION_MANDATORY
-
PROPAGATION_NOT_SUPPORTED
-
PROPAGATION_NEVER
-
PROPAGATION_NESTED
加粗的字体经常使用,需要掌握
5.2事务提交、回滚的时机
- 业务方法执行完,没有异常或错误时,执行提交
- 业务执行过程中,出现编译时异常和error时,执行回滚
- 业务执行过程中,出现编译时异常,执行提交。例如:IoException、FileNotFoundException等。
5.3、Spring事务使用步骤
- 指定使用的事务管理器,在spring中通过
- 指定哪些类,哪些方法需要使用事务
- 指定方法需要的隔离级别、事务传播行为、超时时间
5.4、Spring框架提供的事务方案
5.4.1、@Transactional注解
放在public方法上、给注解属性赋值,表示隔离级别、传播行为、异常信息。
5.4.1.1、@Transactional注解使用步骤
-
声明事务管理器对象
<!-- 声明事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
-
开启事务注解驱动
spring使用aop机制,使用Aop的环绕通知
@Aroud(需要开启事务的方法)
Object myAroud(){
事务管理器对象.commit
try{
管理的对象的方法 ;
事务管理器对象.commit;
}catch(exception e){
事务管理器对象.rollback();
}
}
<!-- 开启事务注解驱动 --> <tx:annotation-driven transaction-manager="transactionManager"/>
-
在方法上或者类上加上@Transactional注解
@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, rollbackFor = {NullPointerException.class,NumOutException.class} ) /** * 默认抛出运行时异常,进行回滚。 */ public void buyGood(int num,String id) {
5.4.1.2、Spring配置文件方式实现事务
适合大型项目,在spring配置文件中声明类,方法需要的事务。
实现步骤
-
导入依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.3.15</version> </dependency>
-
声明事务管理器对象
<!-- 声明事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
-
声明方法需要的事务类型(配置方法的事务属性(隔离级别、传播行为、超时时间))
<tx:advice id="myAdvice" transaction-manager="transactionManager"> <tx:attributes> <!--name:方法名称: 1)完整的方法名称 2)可以统配符表示多个方法名称 isolation:隔离级别 propagation:传播行为 rollback-for:指定全限定异常名称,发生异常一定回滚。 --> <tx:method name="buy*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="java.lang.NullPointerException,com.jfs.exception.NumOutException"/> </tx:attributes> </tx:advice>
-
配置aop声明哪些类,需要创建代理
<!-- 配置aop --> <aop:config> <!-- 配置切入点表达式,指定哪些包中的类,需要使用事务 --> <aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/> <!-- 配置增强器:关联advice和pointcut --> <aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt"/> </aop:config>
6、Spring-Web
6.1、 创建是spring-web项目实现步骤
- 创建一个spring-webapp 的maven项目
- 加入依赖(拷贝spring08-spring-mybatis项目的),并加入jsp和servlet依赖
- 拷贝代码和配置文件
- 创建jsp请求
- 创建一个servlet接收jsp请求,调用service,service调用dao
- 创建一个jsp作为结果页面
需求:web项目中容器对象只需要创建一次,把容器对象放到全局作用域ServletContext中。
实现:使用监听器,当全局作用域对象被创建出来时,把容器对象放到ServletContext中。
-
导入依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.3.14</version> </dependency>
-
注册监听器
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
-
配置监听器加载spring配置文件的路径
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springApplicationConfig.xml</param-value> </context-param>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~