SSM的学习(五)---spring的核心Aop与JdbcTemplate

SSM的学习(五)—spring的核心Aop与JdbcTemplate

Aop

Aop(Aspect Oriented Programming),面向切面编程,是面向对象思想上的补充。
简单来说:就是在原来代码的基础之上,想要不改变源码,添加新的功能,运用场景非常之多如:打印日志 ,事务,等。

Aop的底层原理

aop基于动态代理:会给要改动的对象创建一个代理,通过代理对象来实现代码的增强
java中动态代理有两种实现方式:
jdk动态代理实现


/**
 * 基于jdk动态代理的AOP底层实现
 *   该类功能:对UserDAO 目标类的save方法,做一个增强操作
 *   在保存之前,需要先执行权限验证,具体做法,需要对目标类产生一个代理类对象。
 */
public class JdkProxy implements InvocationHandler {
    private UserDao userDao;

    public JdkProxy(UserDao userDao){
        this.userDao = userDao;
    }

    /**
     * 拿到一个代理对象,改代理对象属于UserDao类型
     * @return
     */
    public UserDao getUserDaoProxy(){
        /**
         * 参数1:目标独享userDao的类加载器对象
         * 参数2:目标独享的接口对象
         * 参数3:实际增强的代码,需参数3完成
         */
        UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(),userDao.getClass().getInterfaces(),this);
        return userDaoProxy;//返回代理对象
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("save".equals(method.getName())){
            //权限验证
            System.out.println("此处 执行权限验证的代码==========");
            return method.invoke(userDao,args);
        }
        return method.invoke(userDao,args);//如果不是save直接放行
    }
}

cglib动态代理实现

import com.spring.anno.UserDao;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibProxy implements MethodInterceptor {
    private UserDao userDao;
    public CglibProxy(UserDao userDao){
        this.userDao = userDao;
    }
    //产生代理对象
    public UserDao getUserDaoProxy(){
        //1:创建Enhancer对象
        Enhancer enhancer = new Enhancer();
        //2:拿到enhancer的父类,因为Cglib是基于继承关系实现的动态代理
        enhancer.setSuperclass(userDao.getClass());
        //3:执行回调,等同于jdk动态代理的InvocationHandler操作
        enhancer.setCallback(this);
        //4:创建代理对象
        return (UserDao)enhancer.create();

    }

    @Override
    /**
     * 增强代码的操作,权限验证的逻辑代码
     */
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        if ("save".equals(method.getName())){
            System.out.println("拿到权限,执行修改操作。。。。");
            return methodProxy.invokeSuper(proxy,args);
        }
        return methodProxy.invokeSuper(proxy,args);
    }
}

实现过程:

创建bean
@Component
public class UserDao {

    public void save(){
        System.out.println("保存用户。。。");
    }

    public void update(){
        System.out.println("更新用户用户。。。");
    }

    public void delete(){
        System.out.println("删除用户。。。");
    }
	
    public void find(){
    //用于测试异常通知
        int a =1;
        int b =0;
        int c = a/b;
        System.out.println("查询用户。。。"+c);
    }
}
xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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/aop
	http://www.springframework.org/schema/aop/spring-aop.xsd
	http://www.springframework.org/schema/tx
	http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--扫描包-->
    <context:component-scan base-package="com.spring"></context:component-scan>
    <!--配置开启Aop切面类的注解-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>
创建切面

@Component
@Aspect
public class MyAspectAnno {

    @Pointcut(value = "execution(* com.spring.anno.UserDao.save(..))")
    private void pointcut(){

    }

    /**
     * 前置通知
     */
    @Before(value = "pointcut()")
    public void before(){
        System.out.println("前置通知。。。。。");
    }

    /**
     * 后置通知
     */
    @After(value = "execution(* com.spring.anno.UserDao.update(..))")
    public void after(){
        System.out.println("后置通知。。。。。");
    }

    /**
     * 返回通知:即目标有返回值时会触发,改注解中的returning属性表示目标方法返回值的变量
     * 这个需要和目标方法的参数类型对应,否则拦截不到,如果想拦截所有,方法的返回值参数可以设置为Object
     * @param result
     */
    @AfterReturning(value = "execution(* com.spring.anno.UserDao.delete(..))" ,returning = "result")
    public void returning(Object result){
        System.out.println("返回通知。。。。。"+result);
    }

    /**
     *
     * @param joinPoint
     * @param e 目标方法所抛出的异常,这个参数是目标方法抛出的异常或异常的父类,否则捕获不到,
     *          如果想拦截所有异常参数类型可以设置为Exception
     */
    @AfterThrowing(value = "execution(* com.spring.anno.UserDao.find(..))",throwing = "e")
    public void throwing(JoinPoint joinPoint,Exception e){
        System.out.println("异常通知。。。。。"+"方法异常"+e.getMessage());
    }

    @Around(value = "execution(* com.spring.anno.UserDao.save(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint){
        System.out.println("环绕通知1。。。。。");
        Object proceed =null;
        try {
            //相当于method.invoke的方法,在该方法前后可以设置类似于前后通知的方法
            proceed = proceedingJoinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("环绕通知2。。。。。");
    }

}
测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class SpringTest {

    @Resource(name = "userDao")
    private UserDao userDao;

    @Test
    public void test(){
        userDao.save();
        userDao.delete();
        userDao.update();
        userDao.find();
    }
}

打印结果:
在这里插入图片描述
AOP的详细分析
那么有人会问了,到底什么时候使用jdk动态代理?什么时候使用cglib动态代理呢?
我们在spring源码中找出了答案:当你动态代理的对象是接口时使用jdk,当是class时则使用cglib。
在这里插入图片描述
于是又有喜欢问问题的要问了:为什么接口只能使用jdk动态代理?
源码中也有答案:因为我们申请的代理对象已经继承了Proxy,(我们都知道java是单继承的吧!)所以无法在继承了,只能继承接口了。
在这里插入图片描述

JdbcTemplate

Spring的jdbc模板
在这里插入图片描述

案例

1:首先创建一个如图的表:
在这里插入图片描述2:创建该表的实体类


public class User {
    private int id;
    private String name;
    private double money;

 	//get、set方法已省略
 	
    public User() {
    }

    public User(int id, String name, double money) {
        this.id = id;
        this.name = name;
        this.money = money;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }

3:创建javaConfig


import com.mchange.v2.c3p0.DriverManagerDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;

@Configuration
public class JdbcConfig {

	/**
	*交给spring容器管理
	*/
    @Bean
    DataSource dataSource(){
        //创建数据库连接池(该连接池是spring框架自己的连接池)
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        //配置连接mysql的操作
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        dataSource.setJdbcUrl("jdbc:mysql:///数据库名");
        dataSource.setUser("账号");
        dataSource.setPassword("密码");
        return dataSource;
    }
	/**
	*创建jdbc模板交给spring管理
	*/
    @Bean
    JdbcTemplate jdbcTemplate(){
        return new JdbcTemplate(dataSource());
    }
}

4:测试


import com.spring.bean.User;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;


import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

public class Main {

    
    private JdbcTemplate jdbcTemplate;

    @BeforeEach //所有测试方法之前调用
    public void before(){
       AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JdbcConfig.class);
        jdbcTemplate = ctx.getBean(JdbcTemplate.class);
   }

    @Test
    public void inseert(){
        jdbcTemplate.update("insert into tb_account(name,money)values(?,?);","Jon",1000d);
    }

    @Test
    public void update(){
        jdbcTemplate.update("update  tb_account set name =?,money =? where id =?","TOM",1000d,5);
    }

    @Test
    public void delete(){
        jdbcTemplate.update("delete from tb_account where id =?",5);
    }

    /**
     * 需要要注意BeanPropertyRowMapper要求查出的字段和bean一致
     */
    @Test
    public void select(){
        User user= jdbcTemplate.queryForObject("select * from tb_account where id = ?",new BeanPropertyRowMapper<User>(User.class),1);
        System.out.println(user);
    }

    /**
     * 自定义RowMapper
     */
    @Test
    public void select1(){
        User user= jdbcTemplate.queryForObject("select * from tb_account where id = ?",new RowMapper<User>(){
            @Override
            public User mapRow(ResultSet rs, int rowNum) throws SQLException {
                int id = rs.getInt("id");
                String name = rs.getString("name");
                Double money = rs.getDouble("money");
                User user = new User();
                user.setId(id);
                user.setName(name);
                user.setMoney(money);
                return user;
            }
        },1);
        System.out.println(user);
    }

    /**
     * 查询多条记录
     */
    @Test
    public void select2(){
        List<User> user = jdbcTemplate.query("select * from tb_account ",new BeanPropertyRowMapper<>(User.class));
        System.out.println(user);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值