Spring框架学习笔记(二)(JdbcTemplate工具类,代理模式)

@[TOC](Spring框架学习笔记(二))

七、JdbcTemplate工具类

Spring中提供了对于JDBC操作的封装,复习JDBC操作数据库步骤:

  1. 加载JDBC驱动(面向接口编程,有SUN公司提供了操作数据库的标准接口,各个数据库厂商实现这套接口):驱动包
  2. 建立数据库连接(因为每次都需要建立,消耗资源,推荐使用开源数据库连接池)
  3. 可变部分:设置指令(SQL语句、函数、存储过程等)
  4. 获取执行SQL语句的对象(StatementPrepareStatement
    1. 变更数据操作:executeUpdate方法,默认情况下,事务是自动提交
    2. 查询操作:JDBC将查询的结果(虚拟表)封装成ResultSet对象,我们可以对该对象进行二次处理
  5. 关闭资源

JdbcTemplate封装了JDBC的基本操作,默认情况下事务自动提交

<dependencies>
    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.49</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.23</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.2.7.RELEASE</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.7.RELEASE</version>
    </dependency>


    <!-- https://mvnrepository.com/artifact/junit/junit -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13</version>
        <scope>test</scope>
    </dependency>

</dependencies>

封装jdbc操作

@Configuration
public class SpringConfig01 {
	@Bean
	public DataSource dataSource(){
		DruidDataSource druidDataSource = new DruidDataSource();
		druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
		druidDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/yf07_mybatis");
		druidDataSource.setUsername("root");
		druidDataSource.setPassword("");
		return druidDataSource;
	}
	@Bean
	public JdbcTemplate jdbcTemplate(DataSource dataSource){
		return new JdbcTemplate(dataSource);
	}
}

常用方法介绍

public class SpringTest {
	private JdbcTemplate jdbcTemplate;
	@Before
	//初始化前执行
	public void init(){
			//加载配置类
			ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig01.class);
		jdbcTemplate = ac.getBean(JdbcTemplate.class);//按照类型获取
	}
	//获取列表数据List<Map<String,Object>>虚拟表中的字段作为Map中的KEY
	@Test
	public void test01(){
		//如果动态的SQL语句,那么这个逻辑判断都是由开发人员通过代码控制
		//名字包含“僧”的数据
		String sql = "SELECT * FROM student WHERE student_name LIKE CONCAT('%',?,'%')";
		List<Map<String, Object>> list = this.jdbcTemplate.queryForList(sql, "僧");
		for (Map<String, Object> map : list) {
			System.out.println("map = " + map);
		}
	}
	@Test
	public void test02(){
		//查询指定student_id的数据
		String sql = "SELECT * FROM student WHERE student_id=?";
		Map<String, Object> map = this.jdbcTemplate.queryForMap(sql, 10);
		System.out.println("map = " + map);
	}
	@Test
	public void test03(){
		//查询指定student_id的数据中student_name单个值
		String sql = "SELECT student_name FROM student WHERE student_id=?";
		String studentName = this.jdbcTemplate.queryForObject(sql, String.class, 10);
		System.out.println("studentName = " + studentName);
		//查询记录总数
		sql = "SELECT COUNT(*) FROM student ";
		Integer count = this.jdbcTemplate.queryForObject(sql, Integer.class);
		System.out.println("count = " + count);
	}
}
@Test
	public void test04(){
		//插入数据  可以参与计算
		String sql = "INSERT INTO student (student_name,student_sex,age,birthday) VALUES (?,?,?+18,?)";
		this.jdbcTemplate.update(sql,"糖糖","女",18,"1990-09-09");
	}
	@Test
	public void test05(){
		String sql = "UPDATE student SET age=age-?";
		this.jdbcTemplate.update(sql,10);
	}

query方法中需要实现RowMapper 接口

因为获取date类型数据的时候默认sql.date,而不是util.date,所以使用rs.getObject获取date数据

  • 匿名实现类
@Test
	public void test06(){
		String sql = "SELECT * FROM student WHERE student_name LIKE CONCAT('%',?,'%')";
		List<Student> list = this.jdbcTemplate.query(sql, new RowMapper<student>(){
	@Override
		public Student mapRow(ResultSet rs, int i) throws SQLException {
			Student student = new Student();
			student.setStudentId(rs.getInt("student_id"));
			student.setStudentName(rs.getString("student_name"));
			student.setAge(rs.getInt("age"));
			student.setBirthday((Date) rs.getObject("birthday"));
			return student;
		}
}, "僧");//匿名实现类
		for (Student student : list) {
			System.out.println("student = " + student);
		}
	}

	}
  • 自己建立实现类,提取公共部分
	@Test
	public void test07(){
		String sql = "SELECT * FROM student WHERE student_id=?";
		Student student = this.jdbcTemplate.queryForObject(sql,new StudentRowMapper(),10);
		System.out.println("student = " + student);
	}
	class StudentRowMapper implements RowMapper<Student>{
		@Override
		public Student mapRow(ResultSet rs, int i) throws SQLException {
			Student student = new Student();
			student.setStudentId(rs.getInt("student_id"));
			student.setStudentName(rs.getString("student_name"));
			student.setAge(rs.getInt("age"));
			student.setBirthday((Date) rs.getObject("birthday"));
			return student;
		}

程序员懒,所有实现类都不想写,所以简化,前提为遵循约定,自动处理:虚拟表中的字段跟类中属性一致 用BeanPropertyRowMapper方法
比较老的系统会使用它,现在基本见不到了

	@Test
	public void test08(){
		String sql = "SELECT student_id studentId,student_name studentName,student_sex studentSex,age,birthday FROM student WHERE student_name LIKE CONCAT('%',?,'%')";
		List<Student> list = this.jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Student.class), "乔");//匿名实现类
		for (Student student : list) {
			System.out.println("student = " + student);
		}
	}
}

八、代理模式

代理模式是一种常见设计模式,并且代理模式是一种思维方式。
前文相关

定义:给某个对象提供一个代理,并且有代理对象控制原对象的引用。

8.1 静态代理

静态代理特别简单,是由程序员编写代理类,并且在程序运行前就编译好,而不是由程序动态产生的代理类,所谓静态代理。

  • 目标类(被代理类)
package com.yue.service;
//目标类(被代理类)
public class UserServiceImpl implements UserService {
	//核心代码已经固定,但是客户增加新的需求
	//假设需求:输入形参的数据
	@Override
	public void add(int age, String account) {
		System.out.println("完成用户的添加操作,固定的核心代码");
	}

	@Override
	public String update(String userName) {
		return userName+userName;
	}
}

如果需求改变,我们可能就需要修改核心代码,就有可能给核心代码造成错误

  • 静态代理类

目的就是创建代理对象
不能让代理看到了核心代码,这是不合理的

不改变原来的核心代码并且增加了新的功能

package com.yue.proxy;

import com.yue.service.UserService;
import com.yue.service.UserServiceImpl;
//代理类是由程序员自己完成
//代理类=>创建代理对象=>代表被代理的对象(目标对象)
public class UserStaticProxy implements UserService {
	private UserServiceImpl target = new UserServiceImpl();//目标对象
	@Override
	public void add(int age, String account) {
		//不合理的,因为代理看到了核心代码(又重写一遍)
		//System.out.println("完成用户的添加操作,固定的核心代码");
		//新增代码,没有改变核心代码
		System.out.println("age = " + age);
		target.add(age,account);//引用目标对象
		System.out.println("account = " + account);
	}

	@Override
	public String update(String userName) {
		System.out.println("userName = " + userName);
		String result = this.target.update(userName);
		return result;
	}
}

缺点:当新增另一个更新功能的时候,接口一增加,目标类和代理类全都要增加

8.2 Java动态代理(必须提供接口)

代理类在程序运行时创建的代理方式被称为动态代理

动态代理,并不是在Java代码中定义,而是在运行时根据我们Java代码中的“指示”动态生成。相比静态代理,动态代理的优势在于可以很方便的代理类的函数进行统一的处理,而不用修改每个代理类中的方法。

代理步骤(固定套路):
  1. 定义一个事务管理类,实现InvocationHadler接口,并且实现invoke(代理类,被代理的方法,方法的形参列表)方法
  2. 实现被代理类以及其实现的接口(Java动态代理,必须存在接口和实现类
  3. 调用Proxy.newProxyInstance(类的加载器,类实现的接口,事务处理器对象)生成代理对象
  4. 通过该代理对象调用方法

InvocationHandler作用就是,当代理对象的原本方法被调用的时候,会绑定执行一个方法,这个方法就是InvocationHandler里面定义的内容,同时会替代原本方法的结果返回。
它接受三个参数:

  1. proxy:代理后的实例对象。
  2. method:对象被调用方法。
  3. args:调用时的参数。
  • 事务管理类

目的:在运行期间动态生成代理类

package com.yue.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;
//运行期间动态生成代理类
public class JavaDynamicProxy implements InvocationHandler {
	private Object target;//目标对象,被代理对象
	public JavaDynamicProxy(Object target){
		this.target = target;
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("新增代码:参数列表 = " + Arrays.toString(args));//统一的处理
		System.out.println("代理的方法:"+method.getName());
		Object result = null;
		result = method.invoke(target,args);
		return result;
	}
}
@Test
public void test02(){
    //被代理对象
    UserServiceImpl userService = new UserServiceImpl();
    //创建代理类
    JavaDynamicProxy javaDynamicProxy = new JavaDynamicProxy(userService);
    //创建代理对象
    //Proxy.newProxyInstance(类的加载器,类实现的接口,事务处理器对象)
    UserService proxy =(UserService) Proxy.newProxyInstance(
        userService.getClass().getClassLoader(),
        userService.getClass().getInterfaces(),
        javaDynamicProxy
    );
    //代理对象调用
    proxy.add(18,"齐天大圣孙悟空");
    String result = proxy.update("八戒");
    System.out.println("result = " + result);
}

结果:
在这里插入图片描述

演示Java动态代理,普通的类,报错:
java.lang.ClassCastException: com.sun.proxy.$Proxy4 cannot be cast to com.yue.service.RoleController

public class RoleController {
	
	public int sum(int a,int b){
		return a+b;
	}
}

此时便需要CGLIB动态代理

8.3 CGLIB(Code Generation Library)动态代理(普通java类不需要接口)

注意:

MethodInterceptor是AOP项目中的拦截器(注:不是动态代理拦截器)
HandlerInterceptor拦截器的区别在于后者拦截目标的请求,它拦截的目标是方法

对类实现代理,对执行目标类(被代理类)动态的生成一个子类(继承方式),子类会重写父类中的方法(增强的代码),所以,被代理的类必须是可以被继承的,被代理的类不能使用final修饰

  • 在pom.xml中添加CGLIB第三方支持
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

依赖图:
在这里插入图片描述

使用生成子类的方式,也就是继承,去实现动态代理

//动态生成代理类(生成子类方式=继承)
public class CGLIBDynamicProxy implements MethodInterceptor {
	//创建代理对象
	public Object getProxy(Class<?> targetClass){
		//1.创建子类(增强代码)
		Enhancer enhancer = new Enhancer();
		//2.设置子类是继承哪个父类
		enhancer.setSuperclass(targetClass);
		//3.定义代理逻辑对象,必须实现MethodInterceptor接口
		enhancer.setCallback(this);
		//4.创建子类对象(代理对象)
		Object proxy = enhancer.create();
		return proxy;
	}

	/**
	 * 拦截所有目标类(被代理类)方法的调用
	 * @param target 目标类的实例对象
	 * @param method 目标方法的反射对象
	 * @param args 方法传递参数
	 * @param proxy 代理类的实例
	 * @return
	 * @throws Throwable
	 */
	@Override
	public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {
		System.out.println("新增代码CGLIB:参数列表 = " + Arrays.toString(args));//统一的处理
		Object result = null;
		result = proxy.invokeSuper(target,args);
		return result;
	}
}

@Test
public void test04(){
    CGLIBDynamicProxy cglibDynamicProxy = new CGLIBDynamicProxy();
    RoleController roleController = (RoleController)cglibDynamicProxy.getProxy(RoleController.class);
    int result01 = roleController.sum(10,20);
    System.out.println("result01 = " + result01);
    UserServiceImpl userService = (UserServiceImpl)cglibDynamicProxy.getProxy(UserServiceImpl.class);
    String result02 = userService.update("八戒");
    System.out.println("result02 = " + result02);
}

8.4 代理对象

代理对象=增强代码+目标对象(被代理的对象),增强代码的可以写在什么位置?

package com.yue.proxy;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import java.util.Arrays;

//动态生成代理类(生成子类方式=继承)
public class CGLIBDynamicProxy implements MethodInterceptor {
	//创建代理对象
	public Object getProxy(Class<?> targetClass){
		//1.创建子类(增强代码)
		Enhancer enhancer = new Enhancer();
		//2.设置子类是继承哪个父类
		enhancer.setSuperclass(targetClass);
		//3.定义代理逻辑对象,必须实现MethodInterceptor接口
		enhancer.setCallback(this);
		//4.创建子类对象(代理对象)
		Object proxy = enhancer.create();
		return proxy;
	}

	/**
	 * 拦截所有目标类(被代理类)方法的调用
	 * @param target 目标类的示例对象
	 * @param method 目标方法的反射对象
	 * @param args 方法传递参数
	 * @param proxy 代理类的实例
	 * @return
	 * @throws Throwable
	 */
	@Override
	public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {
		System.out.println("前置增强:无论代码是否正确,都会执行");
		System.out.println("新增代码CGLIB:参数列表 = " + Arrays.toString(args));//统一的处理
		Object result = null;
		try {
			result = proxy.invokeSuper(target,args);
			System.out.println("返回增强:代码正确的情况,才会执行");
			System.out.println(target.getClass().getSuperclass().getName());
		} catch (Exception ex) {
			ex.printStackTrace();
			System.out.println("异常增强:代码错误的时候,才会执行" );
		}
		System.out.println("后置增强:如果异常没有控制(return/手动抛出异常),那么一定执行");
		return result;
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

月色夜雨

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

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

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

打赏作者

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

抵扣说明:

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

余额充值