Spring
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)
org.mybatis mybatis 3.4.0 mysql mysql-connector-java 5.1.47 org.mybatis mybatis-spring 2.0.6 org.springframework spring-jdbc 5.0.11.RELEASE org.aspectj aspectjweaver 1.9.7 -
编写配置文件
-
配置数据源
-
注册SqlSessionFactory及SqlSessionTemplate(和sqlSession功能一样)
-
将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 ListfindUsers(){ 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"); Listusers = 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" ?> -
编写Mapper.xml映射文件
<?xml version="1.0" encoding="UTF-8" ?>通常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); Listusers = 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注解使用步骤
-
声明事务管理器对象
-
开启事务注解驱动
spring使用aop机制,使用Aop的环绕通知
@Aroud(需要开启事务的方法)
Object myAroud(){
? 事务管理器对象.commit
? try{
? 管理的对象的方法 ;
? 事务管理器对象.commit;
? }catch(exception e){
? 事务管理器对象.rollback();
? }
-
在方法上或者类上加上@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配置文件中声明类,方法需要的事务。
实现步骤
-
导入依赖
org.springframework spring-aspects 5.3.15 -
声明事务管理器对象
-
声明方法需要的事务类型(配置方法的事务属性(隔离级别、传播行为、超时时间))
-
配置aop声明哪些类,需要创建代理
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中。
-
导入依赖
org.springframework spring-web 5.3.14 -
注册监听器
org.springframework.web.context.ContextLoaderListener -
配置监听器加载spring配置文件的路径
contextConfigLocation classpath:springApplicationConfig.xml